Merge "Fix drawable CTS breakages" into mnc-dev
diff --git a/api/current.txt b/api/current.txt
index 376037a..99c5fa0 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3997,24 +3997,22 @@
public class AssistContent implements android.os.Parcelable {
ctor public AssistContent();
method public int describeContents();
- method public static android.app.AssistContent getAssistContent(android.os.Bundle);
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 java.lang.String ASSIST_KEY = "android:assist_content";
field public static final android.os.Parcelable.Creator<android.app.AssistContent> CREATOR;
}
public final class AssistStructure implements android.os.Parcelable {
method public int describeContents();
method public android.content.ComponentName getActivityComponent();
- method public static android.app.AssistStructure getAssistStructure(android.os.Bundle);
method public android.app.AssistStructure.WindowNode getWindowNodeAt(int);
method public int getWindowNodeCount();
method public void writeToParcel(android.os.Parcel, int);
- field public static final java.lang.String ASSIST_KEY = "android:assist_structure";
field public static final android.os.Parcelable.Creator<android.app.AssistStructure> CREATOR;
}
@@ -28916,7 +28914,7 @@
method public android.view.View onCreateContentView();
method public void onDestroy();
method public boolean[] onGetSupportedCommands(android.service.voice.VoiceInteractionSession.Caller, java.lang.String[]);
- method public void onHandleAssist(android.os.Bundle);
+ method public void onHandleAssist(android.os.Bundle, android.app.AssistStructure, android.app.AssistContent);
method public void onHide();
method public boolean onKeyDown(int, android.view.KeyEvent);
method public boolean onKeyLongPress(int, android.view.KeyEvent);
diff --git a/api/system-current.txt b/api/system-current.txt
index e3c7414..588dff8 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4092,24 +4092,22 @@
public class AssistContent implements android.os.Parcelable {
ctor public AssistContent();
method public int describeContents();
- method public static android.app.AssistContent getAssistContent(android.os.Bundle);
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 java.lang.String ASSIST_KEY = "android:assist_content";
field public static final android.os.Parcelable.Creator<android.app.AssistContent> CREATOR;
}
public final class AssistStructure implements android.os.Parcelable {
method public int describeContents();
method public android.content.ComponentName getActivityComponent();
- method public static android.app.AssistStructure getAssistStructure(android.os.Bundle);
method public android.app.AssistStructure.WindowNode getWindowNodeAt(int);
method public int getWindowNodeCount();
method public void writeToParcel(android.os.Parcel, int);
- field public static final java.lang.String ASSIST_KEY = "android:assist_structure";
field public static final android.os.Parcelable.Creator<android.app.AssistStructure> CREATOR;
}
@@ -31042,7 +31040,7 @@
method public android.view.View onCreateContentView();
method public void onDestroy();
method public boolean[] onGetSupportedCommands(android.service.voice.VoiceInteractionSession.Caller, java.lang.String[]);
- method public void onHandleAssist(android.os.Bundle);
+ method public void onHandleAssist(android.os.Bundle, android.app.AssistStructure, android.app.AssistContent);
method public void onHide();
method public boolean onKeyDown(int, android.view.KeyEvent);
method public boolean onKeyLongPress(int, android.view.KeyEvent);
diff --git a/cmds/app_process/Android.mk b/cmds/app_process/Android.mk
index ce6d7b5..3599695 100644
--- a/cmds/app_process/Android.mk
+++ b/cmds/app_process/Android.mk
@@ -52,11 +52,13 @@
LOCAL_CPPFLAGS := -std=c++11
LOCAL_MODULE := app_process__asan
-LOCAL_MODULE_TAGS := eng
-LOCAL_MODULE_PATH := $(TARGET_OUT_EXECUTABLES)/asan
-LOCAL_MODULE_STEM := app_process
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := app_process32
+LOCAL_MODULE_STEM_64 := app_process64
+
LOCAL_ADDRESS_SANITIZER := true
LOCAL_CLANG := true
+LOCAL_MODULE_PATH := $(TARGET_OUT_EXECUTABLES)/asan
LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 02e0d5b..e4def1e 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -2193,7 +2193,9 @@
data.enforceInterface(IActivityManager.descriptor);
IBinder token = data.readStrongBinder();
Bundle extras = data.readBundle();
- reportAssistContextExtras(token, extras);
+ AssistStructure structure = AssistStructure.CREATOR.createFromParcel(data);
+ AssistContent content = AssistContent.CREATOR.createFromParcel(data);
+ reportAssistContextExtras(token, extras, structure, content);
reply.writeNoException();
return true;
}
@@ -5359,13 +5361,15 @@
reply.recycle();
}
- public void reportAssistContextExtras(IBinder token, Bundle extras)
- throws RemoteException {
+ public void reportAssistContextExtras(IBinder token, Bundle extras, AssistStructure structure,
+ AssistContent content) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(token);
data.writeBundle(extras);
+ structure.writeToParcel(data, 0);
+ content.writeToParcel(data, 0);
mRemote.transact(REPORT_ASSIST_CONTEXT_EXTRAS_TRANSACTION, data, reply, 0);
reply.readException();
data.recycle();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index cb436b5..2a98b6c 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -2562,15 +2562,18 @@
public void handleRequestAssistContextExtras(RequestAssistContextExtras cmd) {
Bundle data = new Bundle();
+ AssistStructure structure = null;
+ AssistContent content = new AssistContent();
ActivityClientRecord r = mActivities.get(cmd.activityToken);
if (r != null) {
r.activity.getApplication().dispatchOnProvideAssistData(r.activity, data);
r.activity.onProvideAssistData(data);
if (cmd.requestType == ActivityManager.ASSIST_CONTEXT_FULL) {
- data.putParcelable(AssistStructure.ASSIST_KEY, new AssistStructure(r.activity));
- AssistContent content = new AssistContent();
+ structure = new AssistStructure(r.activity);
Intent activityIntent = r.activity.getIntent();
- if (activityIntent != null) {
+ if (activityIntent != null && (r.window == null ||
+ (r.window.getAttributes().flags
+ & WindowManager.LayoutParams.FLAG_SECURE) == 0)) {
Intent intent = new Intent(activityIntent);
intent.setFlags(intent.getFlags() & ~(Intent.FLAG_GRANT_WRITE_URI_PERMISSION
| Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION));
@@ -2580,15 +2583,14 @@
content.setIntent(new Intent());
}
r.activity.onProvideAssistContent(content);
- data.putParcelable(AssistContent.ASSIST_KEY, content);
}
}
- if (data.isEmpty()) {
- data = null;
+ if (structure == null) {
+ structure = new AssistStructure();
}
IActivityManager mgr = ActivityManagerNative.getDefault();
try {
- mgr.reportAssistContextExtras(cmd.requestToken, data);
+ mgr.reportAssistContextExtras(cmd.requestToken, data, structure, content);
} catch (RemoteException e) {
}
}
diff --git a/core/java/android/app/AssistContent.java b/core/java/android/app/AssistContent.java
index cb1a3f5..f271af1 100644
--- a/core/java/android/app/AssistContent.java
+++ b/core/java/android/app/AssistContent.java
@@ -18,6 +18,7 @@
import android.content.ClipData;
import android.content.Intent;
+import android.net.Uri;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -30,14 +31,17 @@
public class AssistContent implements Parcelable {
private Intent mIntent;
private ClipData mClipData;
+ private Uri mUri;
/**
+ * @hide
* Key name this data structure is stored in the Bundle generated by
* {@link 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}.
*/
@@ -56,6 +60,13 @@
*/
public void setIntent(Intent intent) {
mIntent = intent;
+ setWebUri(null);
+ if (intent != null && Intent.ACTION_VIEW.equals(intent.getAction())) {
+ Uri uri = intent.getData();
+ if ("http".equals(uri.getScheme()) || "https".equals(uri.getScheme())) {
+ setWebUri(uri);
+ }
+ }
}
/**
@@ -81,6 +92,30 @@
return mClipData;
}
+ /**
+ * Set a web URI associated with the current data being shown to the user.
+ * This URI could be opened in a web browser, or in the app as an
+ * {@link Intent#ACTION_VIEW} Intent, to show the same data that is currently
+ * being displayed by it. The URI here should be something that is transportable
+ * off the device into other environments to acesss the same data as is currently
+ * being shown in the app; if the app does not have such a representation, it should
+ * leave the null and only report the local intent and clip data.
+ *
+ * <p>This will be automatically populated for you from {@link #setIntent} if that Intent
+ * is an {@link Intent#ACTION_VIEW} of a web (http or https scheme) URI.</p>
+ */
+ public void setWebUri(Uri uri) {
+ mUri = uri;
+ }
+
+ /**
+ * Return the content's web URI as per {@link #setWebUri(android.net.Uri)}, or null if
+ * there is none.
+ */
+ public Uri getWebUri() {
+ return mUri;
+ }
+
AssistContent(Parcel in) {
if (in.readInt() != 0) {
mIntent = Intent.CREATOR.createFromParcel(in);
@@ -88,6 +123,9 @@
if (in.readInt() != 0) {
mClipData = ClipData.CREATOR.createFromParcel(in);
}
+ if (in.readInt() != 0) {
+ mUri = Uri.CREATOR.createFromParcel(in);
+ }
}
@Override
@@ -109,6 +147,12 @@
} else {
dest.writeInt(0);
}
+ if (mUri != null) {
+ dest.writeInt(1);
+ mUri.writeToParcel(dest, flags);
+ } else {
+ dest.writeInt(0);
+ }
}
public static final Parcelable.Creator<AssistContent> CREATOR
diff --git a/core/java/android/app/AssistStructure.java b/core/java/android/app/AssistStructure.java
index b703b0e..bb9cb3b 100644
--- a/core/java/android/app/AssistStructure.java
+++ b/core/java/android/app/AssistStructure.java
@@ -42,13 +42,13 @@
/**
* Assist data automatically created by the platform's implementation
- * of {@link Activity#onProvideAssistData}. Retrieve it from the assist
- * data with {@link #getAssistStructure(android.os.Bundle)}.
+ * of {@link Activity#onProvideAssistData}.
*/
final public class AssistStructure implements Parcelable {
static final String TAG = "AssistStructure";
/**
+ * @hide
* Key name this data structure is stored in the Bundle generated by
* {@link Activity#onProvideAssistData}.
*/
@@ -741,6 +741,11 @@
}
}
+ AssistStructure() {
+ mHaveData = true;
+ mActivityComponent = null;
+ }
+
AssistStructure(Parcel in) {
mReceiveChannel = in.readStrongBinder();
}
@@ -811,6 +816,7 @@
}
/**
+ * @hide
* Retrieve the framework-generated AssistStructure that is stored within
* the Bundle filled in by {@link Activity#onProvideAssistData}.
*/
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index c42719b..0a425ae 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -433,7 +433,8 @@
public void requestAssistContextExtras(int requestType, IResultReceiver receiver)
throws RemoteException;
- public void reportAssistContextExtras(IBinder token, Bundle extras) throws RemoteException;
+ public void reportAssistContextExtras(IBinder token, Bundle extras,
+ AssistStructure structure, AssistContent content) throws RemoteException;
public boolean launchAssistIntent(Intent intent, int requestType, String hint, int userHandle)
throws RemoteException;
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 4aeab49..0c79094 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -71,6 +71,7 @@
/** Used to communicate with recovery. See bootable/recovery/recovery.c. */
private static File RECOVERY_DIR = new File("/cache/recovery");
private static File COMMAND_FILE = new File(RECOVERY_DIR, "command");
+ private static File UNCRYPT_FILE = new File(RECOVERY_DIR, "uncrypt_file");
private static File LOG_FILE = new File(RECOVERY_DIR, "log");
private static String LAST_PREFIX = "last_";
@@ -333,8 +334,21 @@
public static void installPackage(Context context, File packageFile)
throws IOException {
String filename = packageFile.getCanonicalPath();
+
+ FileWriter uncryptFile = new FileWriter(UNCRYPT_FILE);
+ try {
+ uncryptFile.write(filename + "\n");
+ } finally {
+ uncryptFile.close();
+ }
Log.w(TAG, "!!! REBOOTING TO INSTALL " + filename + " !!!");
+ // If the package is on the /data partition, write the block map file
+ // into COMMAND_FILE instead.
+ if (filename.startsWith("/data/")) {
+ filename = "@/cache/recovery/block.map";
+ }
+
final String filenameArg = "--update_package=" + filename;
final String localeArg = "--locale=" + Locale.getDefault().toString();
bootCommand(context, filenameArg, localeArg);
diff --git a/core/java/android/service/voice/IVoiceInteractionSession.aidl b/core/java/android/service/voice/IVoiceInteractionSession.aidl
index 7c90261..894edac 100644
--- a/core/java/android/service/voice/IVoiceInteractionSession.aidl
+++ b/core/java/android/service/voice/IVoiceInteractionSession.aidl
@@ -16,6 +16,8 @@
package android.service.voice;
+import android.app.AssistContent;
+import android.app.AssistStructure;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Bundle;
@@ -28,7 +30,7 @@
oneway interface IVoiceInteractionSession {
void show(in Bundle sessionArgs, int flags, IVoiceInteractionSessionShowCallback showCallback);
void hide();
- void handleAssist(in Bundle assistData);
+ void handleAssist(in Bundle assistData, in AssistStructure structure, in AssistContent content);
void handleScreenshot(in Bitmap screenshot);
void taskStarted(in Intent intent, int taskId);
void taskFinished(in Intent intent, int taskId);
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index f122d10..f09b6a2 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -16,6 +16,7 @@
package android.service.voice;
+import android.app.AssistContent;
import android.app.AssistStructure;
import android.app.Dialog;
import android.app.Instrumentation;
@@ -180,21 +181,16 @@
}
@Override
- public void handleAssist(Bundle assistBundle) {
+ public void handleAssist(Bundle data, AssistStructure structure,
+ AssistContent content) {
// We want to pre-warm the AssistStructure before handing it off to the main
// thread. There is a strong argument to be made that it should be handed
// through as a separate param rather than part of the assistBundle.
- if (assistBundle != null) {
- Bundle assistContext = assistBundle.getBundle(Intent.EXTRA_ASSIST_CONTEXT);
- if (assistContext != null) {
- AssistStructure as = AssistStructure.getAssistStructure(assistContext);
- if (as != null) {
- as.ensureData();
- }
- }
+ if (structure != null) {
+ structure.ensureData();
}
- mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_HANDLE_ASSIST,
- assistBundle));
+ mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOOO(MSG_HANDLE_ASSIST,
+ data, structure, content));
}
@Override
@@ -422,8 +418,11 @@
doDestroy();
break;
case MSG_HANDLE_ASSIST:
- if (DEBUG) Log.d(TAG, "onHandleAssist: " + msg.obj);
- onHandleAssist((Bundle) msg.obj);
+ args = (SomeArgs)msg.obj;
+ if (DEBUG) Log.d(TAG, "onHandleAssist: data=" + args.arg1
+ + " structure=" + args.arg2 + " content=" + args.arg3);
+ onHandleAssist((Bundle) args.arg1, (AssistStructure) args.arg2,
+ (AssistContent) args.arg3);
break;
case MSG_HANDLE_SCREENSHOT:
if (DEBUG) Log.d(TAG, "onHandleScreenshot: " + msg.obj);
@@ -817,9 +816,22 @@
}
+ /** @hide */
public void onHandleAssist(Bundle assistBundle) {
}
+ public void onHandleAssist(Bundle data, AssistStructure structure, AssistContent content) {
+ if (data != null) {
+ Bundle assistContext = data.getBundle(Intent.EXTRA_ASSIST_CONTEXT);
+ if (assistContext != null) {
+ assistContext.putParcelable(AssistStructure.ASSIST_KEY, structure);
+ assistContext.putParcelable(AssistContent.ASSIST_KEY, content);
+ data.putBundle(Intent.EXTRA_ASSIST_CONTEXT, assistContext);
+ }
+ }
+ onHandleAssist(data);
+ }
+
/** @hide */
public void onHandleScreenshot(Bitmap screenshot) {
}
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 1a76264..d9801ef 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -413,6 +413,10 @@
<!-- Spoken description for ringer normal option. [CHAR LIMIT=NONE] -->
<string name="silent_mode_ring">Ringer on</string>
+ <!-- Reboot to Recovery Progress Dialog. This is shown before it reboots to recovery. -->
+ <string name="reboot_to_recovery_title">Prepare for update</string>
+ <string name="reboot_to_recovery_progress">Processing the update package\u2026</string>
+
<!-- Shutdown Progress Dialog. This is shown if the user chooses to power off the phone. -->
<string name="shutdown_progress">Shutting down\u2026</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e3033e7..ff3801f 100755
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -815,6 +815,8 @@
<java-symbol type="string" name="mobile_provisioning_url" />
<java-symbol type="string" name="mobile_redirected_provisioning_url" />
<java-symbol type="string" name="quick_contacts_not_available" />
+ <java-symbol type="string" name="reboot_to_recovery_progress" />
+ <java-symbol type="string" name="reboot_to_recovery_title" />
<java-symbol type="string" name="reboot_safemode_confirm" />
<java-symbol type="string" name="reboot_safemode_title" />
<java-symbol type="string" name="relationTypeAssistant" />
diff --git a/docs/html/preview/api-overview.jd b/docs/html/preview/api-overview.jd
index 5fd6bb8..b8a6033 100644
--- a/docs/html/preview/api-overview.jd
+++ b/docs/html/preview/api-overview.jd
@@ -165,11 +165,11 @@
<p>To see an app implementation of this feature, refer to the
<a href="https://github.com/googlesamples/android-ConfirmCredentials" class="external-link">
- Confirm Device Credentials sample</a>.</p>
+ Confirm Credentials sample</a>.</p>
<h2 id="direct-share">Direct Share</h2>
-<img src="{@docRoot}preview/images/direct-share-screen_2x.png"
+<img src="{@docRoot}preview/images/direct-share-screen.png"
srcset="{@docRoot}preview/images/direct-share-screen.png 1x, preview/images/direct-share-screen_2x.png 2x"
style="float:right; margin:0 0 20px 30px" width="312" height="335" />
diff --git a/docs/html/preview/testing/performance.jd b/docs/html/preview/testing/performance.jd
index 4ac5a90..003b619 100644
--- a/docs/html/preview/testing/performance.jd
+++ b/docs/html/preview/testing/performance.jd
@@ -554,10 +554,10 @@
</h4>
<p>
- Tool suites like <a href=
- "https://developer.android.com/tools/testing-support-library/index.html">UIAutomator</a>,
- and <a href="https://code.google.com/p/android-test-kit/">Espresso</a> are built to help
- automate the action of a user moving through your application. These are simple
+ Tool suites, like <a href=
+ "{@docRoot}training/testing/ui-testing/uiautomator-testing.html">UI Automator</a> and
+ <a href="{@docRoot}training/testing/ui-testing/espresso-testing.html">Espresso</a>, are
+ built to help automate the action of a user moving through your application. These are simple
frameworks which mimic user interaction with your device. To use these frameworks, you
effectively create unique scripts, which run through a set of user-actions, and play them
out on the device itself.
@@ -585,7 +585,7 @@
<p>
It’s worth noting that UI testing frameworks (like <a href=
- "https://developer.android.com/tools/testing-support-library/index.html">UIAutomator</a>)
+ "{@docRoot}training/testing/ui-testing/uiautomator-testing.html">UI Automator</a>)
run on the target device/emulator directly. While performance gathering information done
by <em>dumpsys gfxinfo</em> is driven by a host machine, sending commands over ADB. To
help bridge the automation of these separate entities, <a href=
@@ -595,7 +595,7 @@
</p>
<p>
- Building a set of scripts for proper Automation of UI Performance testing, at a minimum,
+ Building a set of scripts for proper automation of UI Performance testing, at a minimum,
should be able to utilize monkeyRunner to accomplish the following tasks:
</p>
@@ -603,7 +603,7 @@
<li>Load & Launch a desired APK to a target device, devices, or emulator.
</li>
- <li>Launch a UIAutomator UI test, and allow it to be executed
+ <li>Launch a UI Automator UI test, and allow it to be executed
</li>
<li>Collect performance information through <em>dumpsys gfxinfo</em><em>.</em>
diff --git a/libs/hwui/CanvasState.cpp b/libs/hwui/CanvasState.cpp
index e88e9f6..e22b0d3 100644
--- a/libs/hwui/CanvasState.cpp
+++ b/libs/hwui/CanvasState.cpp
@@ -189,6 +189,9 @@
mSnapshot->setClippingRoundRect(allocator, rect, radius, highPriority);
}
+void CanvasState::setProjectionPathMask(LinearAllocator& allocator, const SkPath* path) {
+ mSnapshot->setProjectionPathMask(allocator, path);
+}
///////////////////////////////////////////////////////////////////////////////
// Quick Rejection
diff --git a/libs/hwui/CanvasState.h b/libs/hwui/CanvasState.h
index 8e4a4d3..9354e94 100644
--- a/libs/hwui/CanvasState.h
+++ b/libs/hwui/CanvasState.h
@@ -130,6 +130,7 @@
void setClippingOutline(LinearAllocator& allocator, const Outline* outline);
void setClippingRoundRect(LinearAllocator& allocator,
const Rect& rect, float radius, bool highPriority = true);
+ void setProjectionPathMask(LinearAllocator& allocator, const SkPath* path);
/**
* Returns true if drawing in the rectangle (left, top, right, bottom)
diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp
index 6fcf958..b077a85 100644
--- a/libs/hwui/DeferredDisplayList.cpp
+++ b/libs/hwui/DeferredDisplayList.cpp
@@ -195,6 +195,7 @@
// Identical round rect clip state means both ops will clip in the same way, or not at all.
// As the state objects are const, we can compare their pointers to determine mergeability
if (lhs->mRoundRectClipState != rhs->mRoundRectClipState) return false;
+ if (lhs->mProjectionPathMask != rhs->mProjectionPathMask) return false;
/* Clipping compatibility check
*
diff --git a/libs/hwui/DeferredDisplayList.h b/libs/hwui/DeferredDisplayList.h
index 3d0ca6d..160c1ad 100644
--- a/libs/hwui/DeferredDisplayList.h
+++ b/libs/hwui/DeferredDisplayList.h
@@ -63,6 +63,7 @@
mat4 mMatrix;
float mAlpha;
const RoundRectClipState* mRoundRectClipState;
+ const ProjectionPathMask* mProjectionPathMask;
};
class OpStatePair {
diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h
index a760135..c152789 100644
--- a/libs/hwui/Matrix.h
+++ b/libs/hwui/Matrix.h
@@ -134,6 +134,12 @@
uint8_t getType() const;
+ void multiplyInverse(const Matrix4& v) {
+ Matrix4 inv;
+ inv.loadInverse(v);
+ multiply(inv);
+ }
+
void multiply(const Matrix4& v) {
Matrix4 u;
u.loadMultiply(*this, v);
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index b3fb7ef..8f91620 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -40,6 +40,7 @@
#include <SkCanvas.h>
#include <SkColor.h>
+#include <SkPathOps.h>
#include <SkShader.h>
#include <SkTypeface.h>
@@ -1193,8 +1194,9 @@
state.mMatrix.load(*currentMatrix);
state.mAlpha = currentSnapshot()->alpha;
- // always store/restore, since it's just a pointer
+ // always store/restore, since these are just pointers
state.mRoundRectClipState = currentSnapshot()->roundRectClipState;
+ state.mProjectionPathMask = currentSnapshot()->projectionPathMask;
return false;
}
@@ -1202,6 +1204,7 @@
setMatrix(state.mMatrix);
writableSnapshot()->alpha = state.mAlpha;
writableSnapshot()->roundRectClipState = state.mRoundRectClipState;
+ writableSnapshot()->projectionPathMask = state.mProjectionPathMask;
if (state.mClipValid && !skipClipRestore) {
writableSnapshot()->setClip(state.mClip.left, state.mClip.top,
@@ -1758,6 +1761,7 @@
void OpenGLRenderer::drawConvexPath(const SkPath& path, const SkPaint* paint) {
VertexBuffer vertexBuffer;
// TODO: try clipping large paths to viewport
+
PathTessellator::tessellatePath(path, paint, *currentTransform(), vertexBuffer);
drawVertexBuffer(vertexBuffer, paint);
}
@@ -1864,19 +1868,41 @@
|| PaintUtils::paintWillNotDraw(*p)) {
return;
}
+
if (p->getPathEffect() != nullptr) {
mCaches.textureState().activateTexture(0);
PathTexture* texture = mCaches.pathCache.getCircle(radius, p);
drawShape(x - radius, y - radius, texture, p);
- } else {
- SkPath path;
- if (p->getStyle() == SkPaint::kStrokeAndFill_Style) {
- path.addCircle(x, y, radius + p->getStrokeWidth() / 2);
- } else {
- path.addCircle(x, y, radius);
- }
- drawConvexPath(path, p);
+ return;
}
+
+ SkPath path;
+ if (p->getStyle() == SkPaint::kStrokeAndFill_Style) {
+ path.addCircle(x, y, radius + p->getStrokeWidth() / 2);
+ } else {
+ path.addCircle(x, y, radius);
+ }
+
+ if (CC_UNLIKELY(currentSnapshot()->projectionPathMask != nullptr)) {
+ // mask ripples with projection mask
+ SkPath maskPath = *(currentSnapshot()->projectionPathMask->projectionMask);
+
+ Matrix4 screenSpaceTransform;
+ currentSnapshot()->buildScreenSpaceTransform(&screenSpaceTransform);
+
+ Matrix4 totalTransform;
+ totalTransform.loadInverse(screenSpaceTransform);
+ totalTransform.multiply(currentSnapshot()->projectionPathMask->projectionMaskTransform);
+
+ SkMatrix skTotalTransform;
+ totalTransform.copyTo(skTotalTransform);
+ maskPath.transform(skTotalTransform);
+
+ // Mask the ripple path by the projection mask, now that it's
+ // in local space. Note that this can create CCW paths.
+ Op(path, maskPath, kIntersect_PathOp, &path);
+ }
+ drawConvexPath(path, p);
}
void OpenGLRenderer::drawOval(float left, float top, float right, float bottom,
@@ -2149,6 +2175,10 @@
mState.setClippingRoundRect(allocator, rect, radius, highPriority);
}
+void OpenGLRenderer::setProjectionPathMask(LinearAllocator& allocator, const SkPath* path) {
+ mState.setProjectionPathMask(allocator, path);
+}
+
void OpenGLRenderer::drawText(const char* text, int bytesCount, int count, float x, float y,
const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds,
DrawOpMode drawOpMode) {
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 218818d..8dae82c 100755
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -399,6 +399,7 @@
void setClippingOutline(LinearAllocator& allocator, const Outline* outline);
void setClippingRoundRect(LinearAllocator& allocator,
const Rect& rect, float radius, bool highPriority = true);
+ void setProjectionPathMask(LinearAllocator& allocator, const SkPath* path);
inline bool hasRectToRectTransform() const { return mState.hasRectToRectTransform(); }
inline const mat4* currentTransform() const { return mState.currentTransform(); }
diff --git a/libs/hwui/PathTessellator.cpp b/libs/hwui/PathTessellator.cpp
index c1f61d6..e7c6c05 100644
--- a/libs/hwui/PathTessellator.cpp
+++ b/libs/hwui/PathTessellator.cpp
@@ -37,6 +37,7 @@
#include <SkPath.h>
#include <SkPaint.h>
+#include <SkPoint.h>
#include <SkGeometry.h> // WARNING: Internal Skia Header
#include <stdlib.h>
@@ -912,6 +913,39 @@
Vertex::set(newVertex, x, y);
}
+class ClockwiseEnforcer {
+public:
+ void addPoint(const SkPoint& point) {
+ double x = point.x();
+ double y = point.y();
+
+ if (initialized) {
+ sum += (x + lastX) * (y - lastY);
+ } else {
+ initialized = true;
+ }
+
+ lastX = x;
+ lastY = y;
+ }
+ void reverseVectorIfNotClockwise(Vector<Vertex>& vertices) {
+ if (sum < 0) {
+ // negative sum implies CounterClockwise
+ const int size = vertices.size();
+ for (int i = 0; i < size / 2; i++) {
+ Vertex tmp = vertices[i];
+ int k = size - 1 - i;
+ vertices.replaceAt(vertices[k], i);
+ vertices.replaceAt(tmp, k);
+ }
+ }
+ }
+private:
+ bool initialized = false;
+ double lastX, lastY;
+ double sum = 0;
+};
+
bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool forceClose,
float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared,
Vector<Vertex>& outputVertices) {
@@ -922,18 +956,22 @@
SkPath::Iter iter(path, forceClose);
SkPoint pts[4];
SkPath::Verb v;
+ ClockwiseEnforcer clockwiseEnforcer;
while (SkPath::kDone_Verb != (v = iter.next(pts))) {
switch (v) {
case SkPath::kMove_Verb:
pushToVector(outputVertices, pts[0].x(), pts[0].y());
ALOGV("Move to pos %f %f", pts[0].x(), pts[0].y());
+ clockwiseEnforcer.addPoint(pts[0]);
break;
case SkPath::kClose_Verb:
ALOGV("Close at pos %f %f", pts[0].x(), pts[0].y());
+ clockwiseEnforcer.addPoint(pts[0]);
break;
case SkPath::kLine_Verb:
ALOGV("kLine_Verb %f %f -> %f %f", pts[0].x(), pts[0].y(), pts[1].x(), pts[1].y());
pushToVector(outputVertices, pts[1].x(), pts[1].y());
+ clockwiseEnforcer.addPoint(pts[1]);
break;
case SkPath::kQuad_Verb:
ALOGV("kQuad_Verb");
@@ -942,6 +980,8 @@
pts[2].x(), pts[2].y(),
pts[1].x(), pts[1].y(),
sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices);
+ clockwiseEnforcer.addPoint(pts[1]);
+ clockwiseEnforcer.addPoint(pts[2]);
break;
case SkPath::kCubic_Verb:
ALOGV("kCubic_Verb");
@@ -951,6 +991,9 @@
pts[3].x(), pts[3].y(),
pts[2].x(), pts[2].y(),
sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices);
+ clockwiseEnforcer.addPoint(pts[1]);
+ clockwiseEnforcer.addPoint(pts[2]);
+ clockwiseEnforcer.addPoint(pts[3]);
break;
case SkPath::kConic_Verb: {
ALOGV("kConic_Verb");
@@ -965,6 +1008,8 @@
quads[offset+1].x(), quads[offset+1].y(),
sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices);
}
+ clockwiseEnforcer.addPoint(pts[1]);
+ clockwiseEnforcer.addPoint(pts[2]);
break;
}
default:
@@ -972,13 +1017,17 @@
}
}
+ bool wasClosed = false;
int size = outputVertices.size();
if (size >= 2 && outputVertices[0].x == outputVertices[size - 1].x &&
outputVertices[0].y == outputVertices[size - 1].y) {
outputVertices.pop();
- return true;
+ wasClosed = true;
}
- return false;
+
+ // ensure output vector is clockwise
+ clockwiseEnforcer.reverseVectorIfNotClockwise(outputVertices);
+ return wasClosed;
}
///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/PathTessellator.h b/libs/hwui/PathTessellator.h
index 8ac9a3b..ccae65b 100644
--- a/libs/hwui/PathTessellator.h
+++ b/libs/hwui/PathTessellator.h
@@ -82,7 +82,7 @@
const mat4& transform, VertexBuffer& vertexBuffer);
/**
- * Approximates a convex, CW outline into a Vector of 2d vertices.
+ * Approximates a convex outline into a clockwise Vector of 2d vertices.
*
* @param path The outline to be approximated
* @param thresholdSquared The threshold of acceptable error (in pixels) when approximating
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 9e5ec28..7d3b41e 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -769,31 +769,9 @@
const RenderProperties& backgroundProps = backgroundOp->mRenderNode->properties();
renderer.translate(backgroundProps.getTranslationX(), backgroundProps.getTranslationY());
- // If the projection reciever has an outline, we mask each of the projected rendernodes to it
- // Either with clipRect, or special saveLayer masking
- if (projectionReceiverOutline != nullptr) {
- const SkRect& outlineBounds = projectionReceiverOutline->getBounds();
- if (projectionReceiverOutline->isRect(nullptr)) {
- // mask to the rect outline simply with clipRect
- ClipRectOp* clipOp = new (alloc) ClipRectOp(
- outlineBounds.left(), outlineBounds.top(),
- outlineBounds.right(), outlineBounds.bottom(), SkRegion::kIntersect_Op);
- handler(clipOp, PROPERTY_SAVECOUNT, properties().getClipToBounds());
- } else {
- // wrap the projected RenderNodes with a SaveLayer that will mask to the outline
- SaveLayerOp* op = new (alloc) SaveLayerOp(
- outlineBounds.left(), outlineBounds.top(),
- outlineBounds.right(), outlineBounds.bottom(),
- 255, SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag | SkCanvas::kARGB_ClipLayer_SaveFlag);
- op->setMask(projectionReceiverOutline);
- handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds());
-
- /* TODO: add optimizations here to take advantage of placement/size of projected
- * children (which may shrink saveLayer area significantly). This is dependent on
- * passing actual drawing/dirtying bounds of projected content down to native.
- */
- }
- }
+ // If the projection reciever has an outline, we mask projected content to it
+ // (which we know, apriori, are all tessellated paths)
+ renderer.setProjectionPathMask(alloc, projectionReceiverOutline);
// draw projected nodes
for (size_t i = 0; i < mProjectedNodes.size(); i++) {
@@ -808,10 +786,8 @@
renderer.restoreToCount(restoreTo);
}
- if (projectionReceiverOutline != nullptr) {
- handler(new (alloc) RestoreToCountOp(restoreTo),
- PROPERTY_SAVECOUNT, properties().getClipToBounds());
- }
+ handler(new (alloc) RestoreToCountOp(restoreTo),
+ PROPERTY_SAVECOUNT, properties().getClipToBounds());
}
/**
diff --git a/libs/hwui/ShadowTessellator.cpp b/libs/hwui/ShadowTessellator.cpp
index fb28531..024ff10 100644
--- a/libs/hwui/ShadowTessellator.cpp
+++ b/libs/hwui/ShadowTessellator.cpp
@@ -158,71 +158,6 @@
}
return result;
}
-/**
- * Test whether the polygon is order in clockwise.
- *
- * @param polygon the polygon as a Vector2 array
- * @param len the number of points of the polygon
- */
-bool ShadowTessellator::isClockwise(const Vector2* polygon, int len) {
- if (len < 2 || polygon == nullptr) {
- return true;
- }
- double sum = 0;
- double p1x = polygon[len - 1].x;
- double p1y = polygon[len - 1].y;
- for (int i = 0; i < len; i++) {
-
- double p2x = polygon[i].x;
- double p2y = polygon[i].y;
- sum += p1x * p2y - p2x * p1y;
- p1x = p2x;
- p1y = p2y;
- }
- return sum < 0;
-}
-
-bool ShadowTessellator::isClockwisePath(const SkPath& path) {
- SkPath::Iter iter(path, false);
- SkPoint pts[4];
- SkPath::Verb v;
-
- Vector<Vector2> arrayForDirection;
- while (SkPath::kDone_Verb != (v = iter.next(pts))) {
- switch (v) {
- case SkPath::kMove_Verb:
- arrayForDirection.add((Vector2){pts[0].x(), pts[0].y()});
- break;
- case SkPath::kLine_Verb:
- arrayForDirection.add((Vector2){pts[1].x(), pts[1].y()});
- break;
- case SkPath::kConic_Verb:
- case SkPath::kQuad_Verb:
- arrayForDirection.add((Vector2){pts[1].x(), pts[1].y()});
- arrayForDirection.add((Vector2){pts[2].x(), pts[2].y()});
- break;
- case SkPath::kCubic_Verb:
- arrayForDirection.add((Vector2){pts[1].x(), pts[1].y()});
- arrayForDirection.add((Vector2){pts[2].x(), pts[2].y()});
- arrayForDirection.add((Vector2){pts[3].x(), pts[3].y()});
- break;
- default:
- break;
- }
- }
-
- return isClockwise(arrayForDirection.array(), arrayForDirection.size());
-}
-
-void ShadowTessellator::reverseVertexArray(Vertex* polygon, int len) {
- int n = len / 2;
- for (int i = 0; i < n; i++) {
- Vertex tmp = polygon[i];
- int k = len - 1 - i;
- polygon[i] = polygon[k];
- polygon[k] = tmp;
- }
-}
int ShadowTessellator::getExtraVertexNumber(const Vector2& vector1,
const Vector2& vector2, float divisor) {
diff --git a/libs/hwui/ShadowTessellator.h b/libs/hwui/ShadowTessellator.h
index c04d8ef..5f4c9c5 100644
--- a/libs/hwui/ShadowTessellator.h
+++ b/libs/hwui/ShadowTessellator.h
@@ -83,23 +83,6 @@
static bool isClockwise(const Vector2* polygon, int len);
static Vector2 calculateNormal(const Vector2& p1, const Vector2& p2);
- /**
- * Determine whether the path is clockwise, using the control points.
- *
- * TODO: Given the skia is using inverted Y coordinate, shadow system needs
- * to convert to the same coordinate to avoid the extra reverse.
- *
- * @param path The path to be examined.
- */
- static bool isClockwisePath(const SkPath &path);
-
- /**
- * Reverse the vertex array.
- *
- * @param polygon The vertex array to be reversed.
- * @param len The length of the vertex array.
- */
- static void reverseVertexArray(Vertex* polygon, int len);
static int getExtraVertexNumber(const Vector2& vector1, const Vector2& vector2,
float divisor);
diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp
index 9e7faee..beb2e1d 100644
--- a/libs/hwui/Snapshot.cpp
+++ b/libs/hwui/Snapshot.cpp
@@ -36,6 +36,7 @@
, empty(false)
, alpha(1.0f)
, roundRectClipState(nullptr)
+ , projectionPathMask(nullptr)
, mClipArea(&mClipAreaRoot) {
transform = &mTransformRoot;
region = nullptr;
@@ -54,6 +55,7 @@
, empty(false)
, alpha(s->alpha)
, roundRectClipState(s->roundRectClipState)
+ , projectionPathMask(s->projectionPathMask)
, mClipArea(nullptr)
, mViewportData(s->mViewportData)
, mRelativeLightCenter(s->mRelativeLightCenter) {
@@ -141,6 +143,34 @@
transform->loadTranslate(x, y, z);
}
+void Snapshot::buildScreenSpaceTransform(Matrix4* outTransform) const {
+ // build (reverse ordered) list of the stack of snapshots, terminated with a NULL
+ Vector<const Snapshot*> snapshotList;
+ snapshotList.push(nullptr);
+ const Snapshot* current = this;
+ do {
+ snapshotList.push(current);
+ current = current->previous.get();
+ } while (current);
+
+ // traverse the list, adding in each transform that contributes to the total transform
+ outTransform->loadIdentity();
+ for (size_t i = snapshotList.size() - 1; i > 0; i--) {
+ // iterate down the stack
+ const Snapshot* current = snapshotList[i];
+ const Snapshot* next = snapshotList[i - 1];
+ if (current->flags & kFlagIsFboLayer) {
+ // if we've hit a layer, translate by the layer's draw offset
+ outTransform->translate(current->layer->layer.left, current->layer->layer.top);
+ }
+ if (!next || (next->flags & kFlagIsFboLayer)) {
+ // if this snapshot is last, or if this snapshot is last before an
+ // FBO layer (which reset the transform), apply it
+ outTransform->multiply(*(current->transform));
+ }
+ }
+}
+
///////////////////////////////////////////////////////////////////////////////
// Clipping round rect
///////////////////////////////////////////////////////////////////////////////
@@ -191,6 +221,18 @@
roundRectClipState = state;
}
+void Snapshot::setProjectionPathMask(LinearAllocator& allocator, const SkPath* path) {
+ if (path) {
+ ProjectionPathMask* mask = new (allocator) ProjectionPathMask;
+ mask->projectionMask = path;
+ buildScreenSpaceTransform(&(mask->projectionMaskTransform));
+
+ projectionPathMask = mask;
+ } else {
+ projectionPathMask = nullptr;
+ }
+}
+
///////////////////////////////////////////////////////////////////////////////
// Queries
///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
index 4d704ab..af6ad72 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -63,6 +63,17 @@
float radius;
};
+class ProjectionPathMask {
+public:
+ /** static void* operator new(size_t size); PURPOSELY OMITTED, allocator only **/
+ static void* operator new(size_t size, LinearAllocator& allocator) {
+ return allocator.alloc(size);
+ }
+
+ const SkPath* projectionMask;
+ Matrix4 projectionMaskTransform;
+};
+
/**
* A snapshot holds information about the current state of the rendering
* surface. A snapshot is usually created whenever the user calls save()
@@ -190,6 +201,11 @@
float radius, bool highPriority);
/**
+ * Sets (and replaces) the current projection mask
+ */
+ void setProjectionPathMask(LinearAllocator& allocator, const SkPath* path);
+
+ /**
* Indicates whether this snapshot should be ignored. A snapshot
* is typically ignored if its layer is invisible or empty.
*/
@@ -201,6 +217,12 @@
bool hasPerspectiveTransform() const;
/**
+ * Fills outTransform with the current, total transform to screen space,
+ * across layer boundaries.
+ */
+ void buildScreenSpaceTransform(Matrix4* outTransform) const;
+
+ /**
* Dirty flags.
*/
int flags;
@@ -272,6 +294,11 @@
*/
const RoundRectClipState* roundRectClipState;
+ /**
+ * Current projection masking path - used exclusively to mask tessellated circles.
+ */
+ const ProjectionPathMask* projectionPathMask;
+
void dump() const;
private:
diff --git a/libs/hwui/TessellationCache.cpp b/libs/hwui/TessellationCache.cpp
index fc173f7..704a691 100644
--- a/libs/hwui/TessellationCache.cpp
+++ b/libs/hwui/TessellationCache.cpp
@@ -207,6 +207,16 @@
transformXY->mapPoint(point.x, point.y);
}
+static void reverseVertexArray(Vertex* polygon, int len) {
+ int n = len / 2;
+ for (int i = 0; i < n; i++) {
+ Vertex tmp = polygon[i];
+ int k = len - 1 - i;
+ polygon[i] = polygon[k];
+ polygon[k] = tmp;
+ }
+}
+
static void tessellateShadows(
const Matrix4* drawTransform, const Rect* localClip,
bool isCasterOpaque, const SkPath* casterPerimeter,
@@ -219,10 +229,9 @@
const float casterRefinementThresholdSquared = 4.0f;
PathTessellator::approximatePathOutlineVertices(*casterPerimeter,
casterRefinementThresholdSquared, casterVertices2d);
- if (!ShadowTessellator::isClockwisePath(*casterPerimeter)) {
- ShadowTessellator::reverseVertexArray(casterVertices2d.editArray(),
- casterVertices2d.size());
- }
+
+ // Shadow requires CCW for now. TODO: remove potential double-reverse
+ reverseVertexArray(casterVertices2d.editArray(), casterVertices2d.size());
if (casterVertices2d.size() == 0) return;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 6e1b434..18954e6 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -39,6 +39,8 @@
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;
@@ -360,6 +362,10 @@
// to respond with the result.
static final int PENDING_ASSIST_EXTRAS_TIMEOUT = 500;
+ // How long top wait when going through the modern assist (which doesn't need to block
+ // on getting this result before starting to launch its UI).
+ static final int PENDING_ASSIST_EXTRAS_LONG_TIMEOUT = 2000;
+
// Maximum number of persisted Uri grants a package is allowed
static final int MAX_PERSISTED_URI_GRANTS = 128;
@@ -477,6 +483,8 @@
public final int userHandle;
public boolean haveResult = false;
public Bundle result = null;
+ public AssistStructure structure = null;
+ public AssistContent content = null;
public PendingAssistExtras(ActivityRecord _activity, Bundle _extras, Intent _intent,
String _hint, IResultReceiver _receiver, int _userHandle) {
activity = _activity;
@@ -10617,7 +10625,7 @@
@Override
public Bundle getAssistContextExtras(int requestType) {
PendingAssistExtras pae = enqueueAssistContext(requestType, null, null, null,
- UserHandle.getCallingUserId());
+ UserHandle.getCallingUserId(), PENDING_ASSIST_EXTRAS_TIMEOUT);
if (pae == null) {
return null;
}
@@ -10639,11 +10647,12 @@
@Override
public void requestAssistContextExtras(int requestType, IResultReceiver receiver) {
- enqueueAssistContext(requestType, null, null, receiver, UserHandle.getCallingUserId());
+ enqueueAssistContext(requestType, null, null, receiver, UserHandle.getCallingUserId(),
+ PENDING_ASSIST_EXTRAS_LONG_TIMEOUT);
}
private PendingAssistExtras enqueueAssistContext(int requestType, Intent intent, String hint,
- IResultReceiver receiver, int userHandle) {
+ IResultReceiver receiver, int userHandle, long timeout) {
enforceCallingPermission(android.Manifest.permission.GET_TOP_ACTIVITY_INFO,
"enqueueAssistContext()");
synchronized (this) {
@@ -10669,7 +10678,7 @@
activity.app.thread.requestAssistContextExtras(activity.appToken, pae,
requestType);
mPendingAssistExtras.add(pae);
- mHandler.postDelayed(pae, PENDING_ASSIST_EXTRAS_TIMEOUT);
+ mHandler.postDelayed(pae, timeout);
} catch (RemoteException e) {
Slog.w(TAG, "getAssistContextExtras failed: crash calling " + activity);
return null;
@@ -10698,10 +10707,13 @@
}
}
- public void reportAssistContextExtras(IBinder token, Bundle extras) {
+ public void reportAssistContextExtras(IBinder token, Bundle extras, AssistStructure structure,
+ AssistContent content) {
PendingAssistExtras pae = (PendingAssistExtras)token;
synchronized (pae) {
pae.result = extras;
+ pae.structure = structure;
+ pae.content = content;
pae.haveResult = true;
pae.notifyAll();
if (pae.intent == null && pae.receiver == null) {
@@ -10721,8 +10733,12 @@
}
if (pae.receiver != null) {
// Caller wants result sent back to them.
+ Bundle topBundle = new Bundle();
+ topBundle.putBundle("data", pae.extras);
+ topBundle.putParcelable("structure", pae.structure);
+ topBundle.putParcelable("content", pae.content);
try {
- pae.receiver.send(0, pae.extras);
+ pae.receiver.send(0, topBundle);
} catch (RemoteException e) {
}
return;
@@ -10741,7 +10757,8 @@
}
public boolean launchAssistIntent(Intent intent, int requestType, String hint, int userHandle) {
- return enqueueAssistContext(requestType, intent, hint, null, userHandle) != null;
+ return enqueueAssistContext(requestType, intent, hint, null, userHandle,
+ PENDING_ASSIST_EXTRAS_TIMEOUT) != null;
}
public void registerProcessObserver(IProcessObserver observer) {
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 5aea746..1b5391e 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -2518,8 +2518,7 @@
/**
* Low-level function to reboot the device. On success, this
* function doesn't return. If more than 20 seconds passes from
- * the time a reboot is requested (120 seconds for reboot to
- * recovery), this method returns.
+ * the time a reboot is requested, this method returns.
*
* @param reason code to pass to the kernel (e.g. "recovery"), or null.
*/
@@ -2527,27 +2526,21 @@
if (reason == null) {
reason = "";
}
- long duration;
if (reason.equals(PowerManager.REBOOT_RECOVERY)) {
// If we are rebooting to go into recovery, instead of
// setting sys.powerctl directly we'll start the
// pre-recovery service which will do some preparation for
// recovery and then reboot for us.
- //
- // This preparation can take more than 20 seconds if
- // there's a very large update package, so lengthen the
- // timeout. We have seen 750MB packages take 3-4 minutes
SystemProperties.set("ctl.start", "pre-recovery");
- duration = 300 * 1000L;
} else {
SystemProperties.set("sys.powerctl", "reboot," + reason);
- duration = 20 * 1000L;
}
try {
- Thread.sleep(duration);
+ Thread.sleep(20 * 1000L);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
+ Slog.wtf(TAG, "Unexpected return from lowLevelReboot!");
}
@Override // Watchdog.Monitor implementation
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index 84eab42..85dd9d4 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-
+
package com.android.server.power;
import android.app.ActivityManagerNative;
@@ -44,6 +44,8 @@
import android.os.SystemVibrator;
import android.os.storage.IMountService;
import android.os.storage.IMountShutdownObserver;
+import android.system.ErrnoException;
+import android.system.Os;
import com.android.internal.telephony.ITelephony;
import com.android.server.pm.PackageManagerService;
@@ -51,6 +53,11 @@
import android.util.Log;
import android.view.WindowManager;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+
public final class ShutdownThread extends Thread {
// constants
private static final String TAG = "ShutdownThread";
@@ -59,14 +66,18 @@
private static final int MAX_BROADCAST_TIME = 10*1000;
private static final int MAX_SHUTDOWN_WAIT_TIME = 20*1000;
private static final int MAX_RADIO_WAIT_TIME = 12*1000;
+ private static final int MAX_UNCRYPT_WAIT_TIME = 15*60*1000;
// length of vibration before shutting down
private static final int SHUTDOWN_VIBRATE_MS = 500;
-
+
// state tracking
private static Object sIsStartedGuard = new Object();
private static boolean sIsStarted = false;
-
+
+ // uncrypt status file
+ private static final String UNCRYPT_STATUS_FILE = "/cache/recovery/uncrypt_status";
+
private static boolean mReboot;
private static boolean mRebootSafeMode;
private static String mRebootReason;
@@ -94,10 +105,11 @@
private Handler mHandler;
private static AlertDialog sConfirmDialog;
-
+ private ProgressDialog mProgressDialog;
+
private ShutdownThread() {
}
-
+
/**
* Request a clean shutdown, waiting for subsystems to clean up their
* state etc. Must be called from a Looper thread in which its UI
@@ -226,7 +238,11 @@
// throw up an indeterminate system dialog to indicate radio is
// shutting down.
ProgressDialog pd = new ProgressDialog(context);
- pd.setTitle(context.getText(com.android.internal.R.string.power_off));
+ if (mRebootReason.equals(PowerManager.REBOOT_RECOVERY)) {
+ pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_recovery_title));
+ } else {
+ pd.setTitle(context.getText(com.android.internal.R.string.power_off));
+ }
pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
pd.setIndeterminate(true);
pd.setCancelable(false);
@@ -234,6 +250,7 @@
pd.show();
+ sInstance.mProgressDialog = pd;
sInstance.mContext = context;
sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
@@ -307,14 +324,14 @@
}
Log.i(TAG, "Sending shutdown broadcast...");
-
+
// First send the high-level shut down broadcast.
mActionDone = false;
Intent intent = new Intent(Intent.ACTION_SHUTDOWN);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
mContext.sendOrderedBroadcastAsUser(intent,
UserHandle.ALL, null, br, mHandler, 0, null, null);
-
+
final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME;
synchronized (mActionDoneSync) {
while (!mActionDone) {
@@ -329,9 +346,9 @@
}
}
}
-
+
Log.i(TAG, "Shutting down activity manager...");
-
+
final IActivityManager am =
ActivityManagerNative.asInterface(ServiceManager.checkService("activity"));
if (am != null) {
@@ -390,9 +407,55 @@
}
}
+ // If it's to reboot into recovery, invoke uncrypt via init service.
+ if (mRebootReason.equals(PowerManager.REBOOT_RECOVERY)) {
+ uncrypt();
+ }
+
rebootOrShutdown(mContext, mReboot, mRebootReason);
}
+ private void prepareUncryptProgress() {
+ // Reset the dialog message to show the decrypt process.
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (mProgressDialog != null) {
+ mProgressDialog.dismiss();
+ }
+ // It doesn't work to change the style of the existing
+ // one. Have to create a new one.
+ ProgressDialog pd = new ProgressDialog(mContext);
+
+ pd.setTitle(mContext.getText(
+ com.android.internal.R.string.reboot_to_recovery_title));
+ pd.setMessage(mContext.getText(
+ com.android.internal.R.string.reboot_to_recovery_progress));
+ pd.setIndeterminate(false);
+ pd.setMax(100);
+ pd.setCancelable(false);
+ pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+ pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
+ pd.setProgressNumberFormat(null);
+ pd.setProgress(0);
+
+ mProgressDialog = pd;
+ mProgressDialog.show();
+ }
+ });
+ }
+
+ private void setUncryptProgress(final int progress) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (mProgressDialog != null) {
+ mProgressDialog.setProgress(progress);
+ }
+ }
+ });
+ }
+
private void shutdownRadios(int timeout) {
// If a radio is wedged, disabling it may hang so we do this work in another thread,
// just in case.
@@ -537,4 +600,78 @@
Log.i(TAG, "Performing low-level shutdown...");
PowerManagerService.lowLevelShutdown();
}
+
+ private void uncrypt() {
+ Log.i(TAG, "Calling uncrypt and monitoring the progress...");
+
+ // Update the ProcessDialog message and style.
+ sInstance.prepareUncryptProgress();
+
+ final boolean[] done = new boolean[1];
+ done[0] = false;
+ Thread t = new Thread() {
+ @Override
+ public void run() {
+ // Create the status pipe file to communicate with /system/bin/uncrypt.
+ new File(UNCRYPT_STATUS_FILE).delete();
+ try {
+ Os.mkfifo(UNCRYPT_STATUS_FILE, 0600);
+ } catch (ErrnoException e) {
+ Log.w(TAG, "ErrnoException when creating named pipe \"" + UNCRYPT_STATUS_FILE +
+ "\": " + e.getMessage());
+ }
+
+ SystemProperties.set("ctl.start", "uncrypt");
+
+ // Read the status from the pipe.
+ try (BufferedReader reader = new BufferedReader(
+ new FileReader(UNCRYPT_STATUS_FILE))) {
+
+ int last_status = Integer.MIN_VALUE;
+ while (true) {
+ String str = reader.readLine();
+ try {
+ int status = Integer.parseInt(str);
+
+ // Avoid flooding the log with the same message.
+ if (status == last_status && last_status != Integer.MIN_VALUE) {
+ continue;
+ }
+ last_status = status;
+
+ if (status >= 0 && status < 100) {
+ // Update status
+ Log.d(TAG, "uncrypt read status: " + status);
+ sInstance.setUncryptProgress(status);
+ } else if (status == 100) {
+ Log.d(TAG, "uncrypt successfully finished.");
+ sInstance.setUncryptProgress(status);
+ break;
+ } else {
+ // Error in /system/bin/uncrypt. Or it's rebooting to recovery
+ // to perform other operations (e.g. factory reset).
+ Log.d(TAG, "uncrypt failed with status: " + status);
+ break;
+ }
+ } catch (NumberFormatException unused) {
+ Log.d(TAG, "uncrypt invalid status received: " + str);
+ break;
+ }
+ }
+ } catch (IOException unused) {
+ Log.w(TAG, "IOException when reading \"" + UNCRYPT_STATUS_FILE + "\".");
+ }
+ done[0] = true;
+ }
+ };
+ t.start();
+
+ try {
+ t.join(MAX_UNCRYPT_WAIT_TIME);
+ } catch (InterruptedException unused) {
+ }
+ if (!done[0]) {
+ Log.w(TAG, "Timed out waiting for uncrypt.");
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 5d8979f..6042c27 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -466,6 +466,8 @@
boolean mShowingBootMessages = false;
boolean mBootAnimationStopped = false;
+ /** Dump of the windows and app tokens at the time of the last ANR. Cleared after
+ * LAST_ANR_LIFETIME_DURATION_MSECS */
String mLastANRState;
/** All DisplayContents in the world, kept here */
@@ -1025,7 +1027,7 @@
private void placeWindowAfter(WindowState pos, WindowState window) {
final WindowList windows = pos.getWindowList();
final int i = windows.indexOf(pos);
- if (true || DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(
+ if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(
TAG, "Adding window " + window + " at "
+ (i+1) + " of " + windows.size() + " (after " + pos + ")");
windows.add(i+1, window);
@@ -1035,7 +1037,7 @@
private void placeWindowBefore(WindowState pos, WindowState window) {
final WindowList windows = pos.getWindowList();
int i = windows.indexOf(pos);
- if (true || DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(
+ if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(
TAG, "Adding window " + window + " at "
+ i + " of " + windows.size() + " (before " + pos + ")");
if (i < 0) {
@@ -1133,7 +1135,7 @@
//apptoken note that the window could be a floating window
//that was created later or a window at the top of the list of
//windows associated with this token.
- if (true || DEBUG_FOCUS_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG,
+ if (DEBUG_FOCUS_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG,
"not Base app: Adding window " + win + " at " + (newIdx + 1) + " of " +
N);
windows.add(newIdx + 1, win);
@@ -1255,7 +1257,7 @@
break;
}
}
- if (true || DEBUG_FOCUS_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG,
+ if (DEBUG_FOCUS_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG,
"Based on layer: Adding window " + win + " at " + (i + 1) + " of " + N);
windows.add(i + 1, win);
mWindowsChanged = true;
@@ -3720,7 +3722,7 @@
atoken.layoutConfigChanges = (configChanges &
(ActivityInfo.CONFIG_SCREEN_SIZE | ActivityInfo.CONFIG_ORIENTATION)) != 0;
atoken.mLaunchTaskBehind = launchTaskBehind;
- if (true || DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG, "addAppToken: " + atoken
+ if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG, "addAppToken: " + atoken
+ " to stack=" + stackId + " task=" + taskId + " at " + addPos);
Task task = mTaskIdToTask.get(taskId);
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
index 03abfba..1117373 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
@@ -20,6 +20,7 @@
import android.app.ActivityManagerNative;
import android.app.AppOpsManager;
import android.app.AssistContent;
+import android.app.AssistStructure;
import android.app.IActivityManager;
import android.content.ClipData;
import android.content.ComponentName;
@@ -291,33 +292,37 @@
return;
}
if (mHaveAssistData) {
+ Bundle assistData;
+ AssistStructure structure;
+ AssistContent content;
if (mAssistData != null) {
+ assistData = mAssistData.getBundle("data");
+ structure = mAssistData.getParcelable("structure");
+ content = mAssistData.getParcelable("content");
int uid = mAssistData.getInt(Intent.EXTRA_ASSIST_UID, -1);
- if (uid >= 0) {
- Bundle assistContext = mAssistData.getBundle(Intent.EXTRA_ASSIST_CONTEXT);
- if (assistContext != null) {
- AssistContent content = AssistContent.getAssistContent(assistContext);
- if (content != null) {
- Intent intent = content.getIntent();
- if (intent != null) {
- ClipData data = intent.getClipData();
- if (data != null && Intent.isAccessUriMode(intent.getFlags())) {
- grantClipDataPermissions(data, intent.getFlags(), uid,
- mCallingUid, mSessionComponentName.getPackageName());
- }
- }
- ClipData data = content.getClipData();
- if (data != null) {
- grantClipDataPermissions(data,
- Intent.FLAG_GRANT_READ_URI_PERMISSION,
- uid, mCallingUid, mSessionComponentName.getPackageName());
- }
+ if (uid >= 0 && content != null) {
+ Intent intent = content.getIntent();
+ if (intent != null) {
+ ClipData data = intent.getClipData();
+ if (data != null && Intent.isAccessUriMode(intent.getFlags())) {
+ grantClipDataPermissions(data, intent.getFlags(), uid,
+ mCallingUid, mSessionComponentName.getPackageName());
}
}
+ ClipData data = content.getClipData();
+ if (data != null) {
+ grantClipDataPermissions(data,
+ Intent.FLAG_GRANT_READ_URI_PERMISSION,
+ uid, mCallingUid, mSessionComponentName.getPackageName());
+ }
}
+ } else {
+ assistData = null;
+ structure = null;
+ content = null;
}
try {
- mSession.handleAssist(mAssistData);
+ mSession.handleAssist(assistData, structure, content);
} catch (RemoteException e) {
}
mAssistData = null;
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 8531944..10cf5c1 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -24,11 +24,11 @@
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
- <uses-sdk android:minSdkVersion="11" />
-
+ <uses-sdk android:minSdkVersion="21" />
+
<application
android:label="HwUi"
- android:hardwareAccelerated="true">
+ android:theme="@android:style/Theme.Material.Light">
<activity
android:name="HwTests"
@@ -42,8 +42,7 @@
<activity
android:name="PathOpsActivity"
- android:label="Path/Ops"
- android:theme="@android:style/Theme.Holo.Light">
+ android:label="Path/Ops">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="com.android.test.hwui.TEST" />
@@ -52,8 +51,7 @@
<activity
android:name="AssetsAtlasActivity"
- android:label="Atlas/Framework"
- android:theme="@android:style/Theme.Holo.Light">
+ android:label="Atlas/Framework">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="com.android.test.hwui.TEST" />
@@ -62,8 +60,7 @@
<activity
android:name="ScaledTextActivity"
- android:label="Text/Scaled"
- android:theme="@android:style/Theme.Holo.Light">
+ android:label="Text/Scaled">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="com.android.test.hwui.TEST" />
@@ -72,8 +69,7 @@
<activity
android:name="Rotate3dTextActivity"
- android:label="Text/3D Rotation"
- android:theme="@android:style/Theme.Holo.Light">
+ android:label="Text/3D Rotation">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="com.android.test.hwui.TEST" />
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
index 3c5c201..70a6336 100644
--- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
@@ -119,11 +119,16 @@
}
@Override
- public void onHandleAssist(Bundle assistBundle) {
- if (assistBundle != null) {
- parseAssistData(assistBundle);
- } else {
- Log.i(TAG, "onHandleAssist: NO ASSIST BUNDLE");
+ public void onHandleAssist(Bundle data, AssistStructure structure, AssistContent content) {
+ mAssistStructure = structure;
+ if (mAssistStructure != null) {
+ if (mAssistVisualizer != null) {
+ mAssistVisualizer.setAssistStructure(mAssistStructure);
+ }
+ }
+ if (content != null) {
+ Log.i(TAG, "Assist intent: " + content.getIntent());
+ Log.i(TAG, "Assist clipdata: " + content.getClipData());
}
}
@@ -139,29 +144,6 @@
}
}
- void parseAssistData(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();
- }
- }
-
void updateState() {
if (mState == STATE_IDLE) {
mTopContent.setVisibility(View.VISIBLE);
diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
index 76019bf..2e515fb 100644
--- a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
+++ b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
@@ -45,7 +45,24 @@
import java.util.Arrays;
import java.util.Map;
-import static com.android.ide.common.rendering.api.RenderResources.*;
+import static android.util.TypedValue.TYPE_ATTRIBUTE;
+import static android.util.TypedValue.TYPE_DIMENSION;
+import static android.util.TypedValue.TYPE_FLOAT;
+import static android.util.TypedValue.TYPE_INT_BOOLEAN;
+import static android.util.TypedValue.TYPE_INT_COLOR_ARGB4;
+import static android.util.TypedValue.TYPE_INT_COLOR_ARGB8;
+import static android.util.TypedValue.TYPE_INT_COLOR_RGB4;
+import static android.util.TypedValue.TYPE_INT_COLOR_RGB8;
+import static android.util.TypedValue.TYPE_INT_DEC;
+import static android.util.TypedValue.TYPE_INT_HEX;
+import static android.util.TypedValue.TYPE_NULL;
+import static android.util.TypedValue.TYPE_REFERENCE;
+import static android.util.TypedValue.TYPE_STRING;
+import static com.android.SdkConstants.PREFIX_RESOURCE_REF;
+import static com.android.SdkConstants.PREFIX_THEME_REF;
+import static com.android.ide.common.rendering.api.RenderResources.REFERENCE_EMPTY;
+import static com.android.ide.common.rendering.api.RenderResources.REFERENCE_NULL;
+import static com.android.ide.common.rendering.api.RenderResources.REFERENCE_UNDEFINED;
/**
* Custom implementation of TypedArray to handle non compiled resources.
@@ -223,7 +240,7 @@
String s = getString(index);
try {
if (s != null) {
- return XmlUtils.convertValueToInt(s, defValue);
+ return convertValueToInt(s, defValue);
}
} catch (NumberFormatException e) {
Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
@@ -764,14 +781,57 @@
}
@Override
+ @SuppressWarnings("ResultOfMethodCallIgnored")
public int getType(int index) {
- if (!hasValue(index)) {
- return TypedValue.TYPE_NULL;
+ String value = getString(index);
+ if (value == null) {
+ return TYPE_NULL;
}
- ResourceValue value = mResourceData[index];
- ResourceType resourceType = value.getResourceType();
- return 0;
- // TODO: fixme.
+ if (value.startsWith(PREFIX_RESOURCE_REF)) {
+ return TYPE_REFERENCE;
+ }
+ if (value.startsWith(PREFIX_THEME_REF)) {
+ return TYPE_ATTRIBUTE;
+ }
+ try {
+ // Don't care about the value. Only called to check if an exception is thrown.
+ convertValueToInt(value, 0);
+ if (value.startsWith("0x") || value.startsWith("0X")) {
+ return TYPE_INT_HEX;
+ }
+ // is it a color?
+ if (value.startsWith("#")) {
+ int length = value.length() - 1;
+ if (length == 3) { // rgb
+ return TYPE_INT_COLOR_RGB4;
+ }
+ if (length == 4) { // argb
+ return TYPE_INT_COLOR_ARGB4;
+ }
+ if (length == 6) { // rrggbb
+ return TYPE_INT_COLOR_RGB8;
+ }
+ if (length == 8) { // aarrggbb
+ return TYPE_INT_COLOR_ARGB8;
+ }
+ }
+ if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false")) {
+ return TYPE_INT_BOOLEAN;
+ }
+ return TYPE_INT_DEC;
+ } catch (NumberFormatException ignored) {
+ try {
+ Float.parseFloat(value);
+ return TYPE_FLOAT;
+ } catch (NumberFormatException ignore) {
+ }
+ // Might be a dimension.
+ if (ResourceHelper.parseFloatAttribute(null, value, new TypedValue(), false)) {
+ return TYPE_DIMENSION;
+ }
+ }
+ // TODO: handle fractions.
+ return TYPE_STRING;
}
/**
@@ -883,6 +943,52 @@
return null;
}
+ /**
+ * Copied from {@link XmlUtils#convertValueToInt(CharSequence, int)}, but adapted to account
+ * for aapt, and the fact that host Java VM's Integer.parseInt("XXXXXXXX", 16) cannot handle
+ * "XXXXXXXX" > 80000000.
+ */
+ private static int convertValueToInt(@Nullable String charSeq, int defValue) {
+ if (null == charSeq)
+ return defValue;
+
+ int sign = 1;
+ int index = 0;
+ int len = charSeq.length();
+ int base = 10;
+
+ if ('-' == charSeq.charAt(0)) {
+ sign = -1;
+ index++;
+ }
+
+ if ('0' == charSeq.charAt(index)) {
+ // Quick check for a zero by itself
+ if (index == (len - 1))
+ return 0;
+
+ char c = charSeq.charAt(index + 1);
+
+ if ('x' == c || 'X' == c) {
+ index += 2;
+ base = 16;
+ } else {
+ index++;
+ // Leave the base as 10. aapt removes the preceding zero, and thus when framework
+ // sees the value, it only gets the decimal value.
+ }
+ } else if ('#' == charSeq.charAt(index)) {
+ return ResourceHelper.getColor(charSeq) * sign;
+ } else if ("true".equals(charSeq) || "TRUE".equals(charSeq)) {
+ return -1;
+ } else if ("false".equals(charSeq) || "FALSE".equals(charSeq)) {
+ return 0;
+ }
+
+ // Use Long, since we want to handle hex ints > 80000000.
+ return ((int)Long.parseLong(charSeq.substring(index), base)) * sign;
+ }
+
static TypedArray obtain(Resources res, int len) {
return res instanceof BridgeResources ?
new BridgeTypedArray(((BridgeResources) res), null, len, true) : null;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 9d2b884..f03ec58 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -130,6 +130,7 @@
new IdentityHashMap<Object, Map<String,String>>();
// maps for dynamically generated id representing style objects (StyleResourceValue)
+ @Nullable
private Map<Integer, StyleResourceValue> mDynamicIdToStyleMap;
private Map<StyleResourceValue, Integer> mStyleToDynamicIdMap;
private int mDynamicIdGenerator = 0x02030000; // Base id for R.style in custom namespace
@@ -727,7 +728,7 @@
}
}
} else if (defStyleRes != 0) {
- StyleResourceValue item = mDynamicIdToStyleMap.get(defStyleRes);
+ StyleResourceValue item = getStyleByDynamicId(defStyleRes);
if (item != null) {
defStyleValues = item;
} else {