Merge "Fix build: documentation error in AlarmClock.java." into mnc-dev
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index eafcdb2..9c0d931 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -24,7 +24,10 @@
import android.content.Intent;
import android.content.IntentSender;
import android.graphics.SurfaceTexture;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.os.IBinder;
+import android.os.Message;
import android.os.OperationCanceledException;
import android.os.RemoteException;
import android.util.AttributeSet;
@@ -48,7 +51,9 @@
private static final String TAG = "ActivityView";
private static final boolean DEBUG = false;
- DisplayMetrics mMetrics;
+ private static final int MSG_SET_SURFACE = 1;
+
+ DisplayMetrics mMetrics = new DisplayMetrics();
private final TextureView mTextureView;
private ActivityContainerWrapper mActivityContainer;
private Activity mActivity;
@@ -58,6 +63,9 @@
private int mLastVisibility;
private ActivityViewCallback mActivityViewCallback;
+ private HandlerThread mThread = new HandlerThread("ActivityViewThread");
+ private Handler mHandler;
+
public ActivityView(Context context) {
this(context, null);
}
@@ -89,12 +97,27 @@
+ e);
}
+ mThread.start();
+ mHandler = new Handler(mThread.getLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ super.handleMessage(msg);
+ if (msg.what == MSG_SET_SURFACE) {
+ try {
+ mActivityContainer.setSurface((Surface) msg.obj, msg.arg1, msg.arg2,
+ mMetrics.densityDpi);
+ } catch (RemoteException e) {
+ throw new RuntimeException(
+ "ActivityView: Unable to set surface of ActivityContainer. " + e);
+ }
+ }
+ }
+ };
mTextureView = new TextureView(context);
mTextureView.setSurfaceTextureListener(new ActivityViewSurfaceTextureListener());
addView(mTextureView);
WindowManager wm = (WindowManager)mActivity.getSystemService(Context.WINDOW_SERVICE);
- mMetrics = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(mMetrics);
mLastVisibility = getVisibility();
@@ -111,18 +134,12 @@
protected void onVisibilityChanged(View changedView, int visibility) {
super.onVisibilityChanged(changedView, visibility);
- if (mSurface != null) {
- try {
- if (visibility == View.GONE) {
- mActivityContainer.setSurface(null, mWidth, mHeight, mMetrics.densityDpi);
- } else if (mLastVisibility == View.GONE) {
- // Don't change surface when going between View.VISIBLE and View.INVISIBLE.
- mActivityContainer.setSurface(mSurface, mWidth, mHeight, mMetrics.densityDpi);
- }
- } catch (RemoteException e) {
- throw new RuntimeException(
- "ActivityView: Unable to set surface of ActivityContainer. " + e);
- }
+ if (mSurface != null && (visibility == View.GONE || mLastVisibility == View.GONE)) {
+ Message msg = Message.obtain(mHandler, MSG_SET_SURFACE);
+ msg.obj = (visibility == View.GONE) ? null : mSurface;
+ msg.arg1 = mWidth;
+ msg.arg2 = mHeight;
+ mHandler.sendMessage(msg);
}
mLastVisibility = visibility;
}
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index b2c1c71..bc625dd 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -169,6 +169,9 @@
private final HashSet<Surface> mSurfaceSet;
private final CameraMetadataNative mSettings;
private boolean mIsReprocess;
+ // If this request is part of constrained high speed request list that was created by
+ // {@link CameraDevice#createConstrainedHighSpeedRequestList}.
+ private boolean mIsPartOfCHSRequestList = false;
// Each reprocess request must be tied to a reprocessable session ID.
// Valid only for reprocess requests (mIsReprocess == true).
private int mReprocessableSessionId;
@@ -197,6 +200,7 @@
mSettings = new CameraMetadataNative(source.mSettings);
mSurfaceSet = (HashSet<Surface>) source.mSurfaceSet.clone();
mIsReprocess = source.mIsReprocess;
+ mIsPartOfCHSRequestList = source.mIsPartOfCHSRequestList;
mReprocessableSessionId = source.mReprocessableSessionId;
mUserTag = source.mUserTag;
}
@@ -321,6 +325,35 @@
}
/**
+ * <p>Determine if this request is part of a constrained high speed request list that was
+ * created by {@link CameraDevice#createConstrainedHighSpeedRequestList}. A constrained high
+ * speed request list contains some constrained high speed capture requests with certain
+ * interleaved pattern that is suitable for high speed preview/video streaming. An active
+ * constrained high speed capture session only accepts constrained high speed request lists.
+ * This method can be used to do the sanity check when a constrained high speed capture session
+ * receives a request list via {@link CameraCaptureSession#setRepeatingBurst} or
+ * {@link CameraCaptureSession#captureBurst}.
+ * </p>
+ *
+ *
+ * @return {@code true} if this request is part of a constrained high speed request list,
+ * {@code false} otherwise.
+ *
+ * @hide
+ */
+ public boolean isPartOfCRequestList() {
+ return mIsPartOfCHSRequestList;
+ }
+
+ /**
+ * Returns a copy of the underlying {@link CameraMetadataNative}.
+ * @hide
+ */
+ public CameraMetadataNative getNativeCopy() {
+ return new CameraMetadataNative(mSettings);
+ }
+
+ /**
* Get the reprocessable session ID this reprocess capture request is associated with.
*
* @return the reprocessable session ID this reprocess capture request is associated with
@@ -547,6 +580,18 @@
}
/**
+ * <p>Mark this request as part of a constrained high speed request list created by
+ * {@link CameraDevice#createConstrainedHighSpeedRequestList}. A constrained high speed
+ * request list contains some constrained high speed capture requests with certain
+ * interleaved pattern that is suitable for high speed preview/video streaming.</p>
+ *
+ * @hide
+ */
+ public void setPartOfCHSRequestList(boolean partOfCHSList) {
+ mRequest.mIsPartOfCHSRequestList = partOfCHSList;
+ }
+
+ /**
* Build a request using the current target Surfaces and settings.
* <p>Note that, although it is possible to create a {@code CaptureRequest} with no target
* {@link Surface}s, passing such a request into {@link CameraCaptureSession#capture},
@@ -563,7 +608,6 @@
return new CaptureRequest(mRequest);
}
-
/**
* @hide
*/
diff --git a/core/java/android/hardware/camera2/ICameraDeviceUser.aidl b/core/java/android/hardware/camera2/ICameraDeviceUser.aidl
index 375b310..1574f93 100644
--- a/core/java/android/hardware/camera2/ICameraDeviceUser.aidl
+++ b/core/java/android/hardware/camera2/ICameraDeviceUser.aidl
@@ -61,7 +61,7 @@
* must be called before any requests can be submitted.
* <p>
*/
- int endConfigure();
+ int endConfigure(boolean isConstrainedHighSpeed);
int deleteStream(int streamId);
diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
index ab0f607..cbc85f3 100644
--- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
@@ -60,6 +60,7 @@
private final android.hardware.camera2.impl.CameraDeviceImpl mDeviceImpl;
/** Internal handler; used for all incoming events to preserve total order */
private final Handler mDeviceHandler;
+ private final boolean mIsConstrainedHighSpeedSession;
/** Drain Sequence IDs which have been queued but not yet finished with aborted/completed */
private final TaskDrainer<Integer> mSequenceDrainer;
@@ -88,13 +89,14 @@
CameraCaptureSessionImpl(int id, Surface input, List<Surface> outputs,
CameraCaptureSession.StateCallback callback, Handler stateHandler,
android.hardware.camera2.impl.CameraDeviceImpl deviceImpl,
- Handler deviceStateHandler, boolean configureSuccess) {
+ Handler deviceStateHandler, boolean configureSuccess, boolean isConstrainedHighSpeed) {
if (outputs == null || outputs.isEmpty()) {
throw new IllegalArgumentException("outputs must be a non-null, non-empty list");
} else if (callback == null) {
throw new IllegalArgumentException("callback must not be null");
}
+ mIsConstrainedHighSpeedSession = isConstrainedHighSpeed;
mId = id;
mIdString = String.format("Session %d: ", mId);
@@ -134,6 +136,30 @@
}
}
+
+ private boolean isConstrainedHighSpeedRequestList(List<CaptureRequest> requestList) {
+ checkCollectionNotEmpty(requestList, "High speed request list");
+ for (CaptureRequest request : requestList) {
+ if (!request.isPartOfCRequestList()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * If the session is constrained high speed session, it only accept constrained high speed
+ * request list.
+ */
+ private void checkConstrainedHighSpeedRequestSanity(List<CaptureRequest> requestList) {
+ if (mIsConstrainedHighSpeedSession) {
+ if (!isConstrainedHighSpeedRequestList(requestList)) {
+ throw new IllegalArgumentException("It is only allowed to submit a constrained "
+ + "high speed request list to a constrianed high speed session!!!");
+ }
+ }
+ }
+
@Override
public CameraDevice getDevice() {
return mDeviceImpl;
@@ -155,6 +181,10 @@
} else if (request.isReprocess() && request.getReprocessableSessionId() != mId) {
throw new IllegalArgumentException("capture request was created for another session");
}
+ if (mIsConstrainedHighSpeedSession) {
+ throw new UnsupportedOperationException("Constrained high speed session doesn't support"
+ + " this method");
+ }
checkNotClosed();
@@ -178,6 +208,8 @@
throw new IllegalArgumentException("Requests must have at least one element");
}
+ checkConstrainedHighSpeedRequestSanity(requests);
+
for (CaptureRequest request : requests) {
if (request.isReprocess()) {
if (!isReprocessable()) {
@@ -212,7 +244,10 @@
} else if (request.isReprocess()) {
throw new IllegalArgumentException("repeating reprocess requests are not supported");
}
-
+ if (mIsConstrainedHighSpeedSession) {
+ throw new UnsupportedOperationException("Constrained high speed session doesn't support"
+ + " this method");
+ }
checkNotClosed();
@@ -236,6 +271,8 @@
throw new IllegalArgumentException("requests must have at least one element");
}
+ checkConstrainedHighSpeedRequestSanity(requests);
+
for (CaptureRequest r : requests) {
if (r.isReprocess()) {
throw new IllegalArgumentException("repeating reprocess burst requests are not " +
@@ -704,7 +741,8 @@
// everything is idle.
try {
// begin transition to unconfigured
- mDeviceImpl.configureStreamsChecked(null, null);
+ mDeviceImpl.configureStreamsChecked(/*inputConfig*/null, /*outputs*/null,
+ /*isConstrainedHighSpeed*/false);
} catch (CameraAccessException e) {
// OK: do not throw checked exceptions.
Log.e(TAG, mIdString + "Exception while unconfiguring outputs: ", e);
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index ad0cd0f..c073ba5 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -18,6 +18,7 @@
import static android.hardware.camera2.CameraAccessException.CAMERA_IN_USE;
+import android.graphics.ImageFormat;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
@@ -35,17 +36,22 @@
import android.hardware.camera2.utils.CameraBinderDecorator;
import android.hardware.camera2.utils.CameraRuntimeException;
import android.hardware.camera2.utils.LongParcelable;
+import android.hardware.camera2.utils.SurfaceUtils;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.util.Log;
+import android.util.Range;
import android.util.Size;
import android.util.SparseArray;
import android.view.Surface;
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -326,7 +332,8 @@
for (Surface s : outputs) {
outputConfigs.add(new OutputConfiguration(s));
}
- configureStreamsChecked(/*inputConfig*/null, outputConfigs);
+ configureStreamsChecked(/*inputConfig*/null, outputConfigs,
+ /*isConstrainedHighSpeed*/false);
}
@@ -344,12 +351,14 @@
*
* @param inputConfig input configuration or {@code null} for no input
* @param outputs a list of one or more surfaces, or {@code null} to unconfigure
+ * @param isConstrainedHighSpeed If the streams configuration is for constrained high speed output.
* @return whether or not the configuration was successful
*
* @throws CameraAccessException if there were any unexpected problems during configuration
*/
public boolean configureStreamsChecked(InputConfiguration inputConfig,
- List<OutputConfiguration> outputs) throws CameraAccessException {
+ List<OutputConfiguration> outputs, boolean isConstrainedHighSpeed)
+ throws CameraAccessException {
// Treat a null input the same an empty list
if (outputs == null) {
outputs = new ArrayList<OutputConfiguration>();
@@ -422,7 +431,7 @@
}
try {
- mRemoteDevice.endConfigure();
+ mRemoteDevice.endConfigure(isConstrainedHighSpeed);
}
catch (IllegalArgumentException e) {
// OK. camera service can reject stream config if it's not supported by HAL
@@ -463,7 +472,8 @@
for (Surface surface : outputs) {
outConfigurations.add(new OutputConfiguration(surface));
}
- createCaptureSessionInternal(null, outConfigurations, callback, handler);
+ createCaptureSessionInternal(null, outConfigurations, callback, handler,
+ /*isConstrainedHighSpeed*/false);
}
@Override
@@ -475,7 +485,8 @@
Log.d(TAG, "createCaptureSessionByOutputConfiguration");
}
- createCaptureSessionInternal(null, outputConfigurations, callback, handler);
+ createCaptureSessionInternal(null, outputConfigurations, callback, handler,
+ /*isConstrainedHighSpeed*/false);
}
@Override
@@ -494,13 +505,14 @@
for (Surface surface : outputs) {
outConfigurations.add(new OutputConfiguration(surface));
}
- createCaptureSessionInternal(inputConfig, outConfigurations, callback, handler);
+ createCaptureSessionInternal(inputConfig, outConfigurations, callback, handler,
+ /*isConstrainedHighSpeed*/false);
}
private void createCaptureSessionInternal(InputConfiguration inputConfig,
List<OutputConfiguration> outputConfigurations,
- CameraCaptureSession.StateCallback callback, Handler handler)
- throws CameraAccessException {
+ CameraCaptureSession.StateCallback callback, Handler handler,
+ boolean isConstrainedHighSpeed) throws CameraAccessException {
synchronized(mInterfaceLock) {
if (DEBUG) {
Log.d(TAG, "createCaptureSessionInternal");
@@ -508,6 +520,11 @@
checkIfCameraClosedOrInError();
+ if (isConstrainedHighSpeed && inputConfig != null) {
+ throw new IllegalArgumentException("Constrained high speed session doesn't support"
+ + " input configuration yet.");
+ }
+
// Notify current session that it's going away, before starting camera operations
// After this call completes, the session is not allowed to call into CameraDeviceImpl
if (mCurrentSession != null) {
@@ -520,7 +537,8 @@
Surface input = null;
try {
// configure streams and then block until IDLE
- configureSuccess = configureStreamsChecked(inputConfig, outputConfigurations);
+ configureSuccess = configureStreamsChecked(inputConfig, outputConfigurations,
+ isConstrainedHighSpeed);
if (inputConfig != null) {
input = new Surface();
mRemoteDevice.getInputSurface(/*out*/input);
@@ -545,7 +563,7 @@
CameraCaptureSessionImpl newSession =
new CameraCaptureSessionImpl(mNextSessionId++, input,
outSurfaces, callback, handler, this, mDeviceHandler,
- configureSuccess);
+ configureSuccess, isConstrainedHighSpeed);
// TODO: wait until current session closes, then create the new session
mCurrentSession = newSession;
@@ -1906,17 +1924,156 @@
return mCharacteristics;
}
+ private void checkConstrainedHighSpeedSurfaces(Collection<Surface> surfaces,
+ Range<Integer> fpsRange) {
+ if (surfaces == null || surfaces.size() == 0 || surfaces.size() > 2) {
+ throw new IllegalArgumentException("Output target surface list must not be null and"
+ + " the size must be 1 or 2");
+ }
+
+ StreamConfigurationMap config =
+ getCharacteristics().get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+ List<Size> highSpeedSizes = null;
+ if (fpsRange == null) {
+ highSpeedSizes = Arrays.asList(config.getHighSpeedVideoSizes());
+ } else {
+ // Check the FPS range first if provided
+ Range<Integer>[] highSpeedFpsRanges = config.getHighSpeedVideoFpsRanges();
+ if(!Arrays.asList(highSpeedFpsRanges).contains(fpsRange)) {
+ throw new IllegalArgumentException("Fps range " + fpsRange.toString() + " in the"
+ + " request is not a supported high speed fps range " +
+ Arrays.toString(highSpeedFpsRanges));
+ }
+ highSpeedSizes = Arrays.asList(config.getHighSpeedVideoSizesFor(fpsRange));
+ }
+
+ for (Surface surface : surfaces) {
+ // Surface size must be supported high speed sizes.
+ Size surfaceSize = SurfaceUtils.getSurfaceSize(surface);
+ int surfaceFormat = SurfaceUtils.getSurfaceFormat(surface);
+
+ if (surfaceFormat != ImageFormat.PRIVATE) {
+ throw new IllegalArgumentException("Surface format is not for preview or"
+ + " hardware video encoding" + surfaceFormat);
+ }
+
+ if (!highSpeedSizes.contains(surfaceSize)) {
+ throw new IllegalArgumentException("Surface size " + surfaceSize.toString() + " is"
+ + " not part of the high speed supported size list " +
+ Arrays.toString(highSpeedSizes.toArray()));
+ }
+ // Each output surface must be either preview surface or recording surface.
+ if (!SurfaceUtils.isSurfaceForPreview(surface) &&
+ !SurfaceUtils.isSurfaceForHwVideoEncoder(surface)) {
+ throw new IllegalArgumentException("This output surface is neither preview nor "
+ + "hardware video encoding surface");
+ }
+ if (SurfaceUtils.isSurfaceForPreview(surface) &&
+ SurfaceUtils.isSurfaceForHwVideoEncoder(surface)) {
+ throw new IllegalArgumentException("This output surface can not be both preview"
+ + " and hardware video encoding surface");
+ }
+ }
+
+ // For 2 output surface case, they shouldn't be same type.
+ if (surfaces.size() == 2) {
+ // Up to here, each surface can only be either preview or recording.
+ Iterator<Surface> iterator = surfaces.iterator();
+ boolean isFirstSurfacePreview =
+ SurfaceUtils.isSurfaceForPreview(iterator.next());
+ boolean isSecondSurfacePreview =
+ SurfaceUtils.isSurfaceForPreview(iterator.next());
+ if (isFirstSurfacePreview == isSecondSurfacePreview) {
+ throw new IllegalArgumentException("The 2 output surfaces must have different"
+ + " type");
+ }
+ }
+ }
+
@Override
public void createConstrainedHighSpeedCaptureSession(List<Surface> outputs,
android.hardware.camera2.CameraCaptureSession.StateCallback callback, Handler handler)
throws CameraAccessException {
- // TODO: to be implemented
- throw new UnsupportedOperationException("To be implemented!!!!");
+ if (outputs == null || outputs.size() == 0 || outputs.size() > 2) {
+ throw new IllegalArgumentException(
+ "Output surface list must not be null and the size must be no more than 2");
+ }
+ checkConstrainedHighSpeedSurfaces(outputs, /*fpsRange*/null);
+
+ List<OutputConfiguration> outConfigurations = new ArrayList<>(outputs.size());
+ for (Surface surface : outputs) {
+ outConfigurations.add(new OutputConfiguration(surface));
+ }
+ createCaptureSessionInternal(null, outConfigurations, callback, handler,
+ /*isConstrainedHighSpeed*/true);
}
@Override
public List<CaptureRequest> createConstrainedHighSpeedRequestList(CaptureRequest request)
throws CameraAccessException {
- throw new UnsupportedOperationException("To be implemented!!!!");
+ if (request == null) {
+ throw new IllegalArgumentException("Input capture request must not be null");
+ }
+ Collection<Surface> outputSurfaces = request.getTargets();
+ Range<Integer> fpsRange = request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE);
+ checkConstrainedHighSpeedSurfaces(outputSurfaces, fpsRange);
+
+ // Request list size: to limit the preview to 30fps, need use maxFps/30; to maximize
+ // the preview frame rate, should use maxBatch size for that high speed stream
+ // configuration. We choose the former for now.
+ int requestListSize = fpsRange.getUpper() / 30;
+ List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
+
+ // Prepare the Request builders: need carry over the request controls.
+ // First, create a request builder that will only include preview or recording target.
+ CameraMetadataNative requestMetadata = new CameraMetadataNative(request.getNativeCopy());
+ CaptureRequest.Builder singleTargetRequestBuilder = new CaptureRequest.Builder(
+ requestMetadata, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE);
+
+ // Overwrite the capture intent to make sure a good value is set.
+ Surface[] surfaces = (Surface[])outputSurfaces.toArray();
+ if (outputSurfaces.size() == 1 && SurfaceUtils.isSurfaceForHwVideoEncoder(surfaces[0])) {
+ singleTargetRequestBuilder.set(CaptureRequest.CONTROL_CAPTURE_INTENT,
+ CaptureRequest.CONTROL_CAPTURE_INTENT_PREVIEW);
+ } else {
+ // Video only, or preview + video
+ singleTargetRequestBuilder.set(CaptureRequest.CONTROL_CAPTURE_INTENT,
+ CaptureRequest.CONTROL_CAPTURE_INTENT_VIDEO_RECORD);
+ }
+ singleTargetRequestBuilder.setPartOfCHSRequestList(/*partOfCHSList*/true);
+
+ // Second, Create a request builder that will include both preview and recording targets.
+ CaptureRequest.Builder doubleTargetRequestBuilder = null;
+ if (outputSurfaces.size() == 2) {
+ doubleTargetRequestBuilder = new CaptureRequest.Builder(
+ requestMetadata, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE);
+ doubleTargetRequestBuilder.set(CaptureRequest.CONTROL_CAPTURE_INTENT,
+ CaptureRequest.CONTROL_CAPTURE_INTENT_VIDEO_RECORD);
+ doubleTargetRequestBuilder.addTarget(surfaces[0]);
+ doubleTargetRequestBuilder.addTarget(surfaces[1]);
+ doubleTargetRequestBuilder.setPartOfCHSRequestList(/*partOfCHSList*/true);
+ // Make sure singleTargetRequestBuilder contains only recording surface for
+ // preview + recording case.
+ Surface recordingSurface = surfaces[0];
+ if (!SurfaceUtils.isSurfaceForHwVideoEncoder(recordingSurface)) {
+ recordingSurface = surfaces[1];
+ }
+ singleTargetRequestBuilder.addTarget(recordingSurface);
+ } else {
+ // Single output case: either recording or preview.
+ singleTargetRequestBuilder.addTarget(surfaces[0]);
+ }
+
+ // Generate the final request list.
+ for (int i = 0; i < requestListSize; i++) {
+ if (i == 0 && doubleTargetRequestBuilder != null) {
+ // First request should be recording + preview request
+ requestList.add(doubleTargetRequestBuilder.build());
+ } else {
+ requestList.add(singleTargetRequestBuilder.build());
+ }
+ }
+
+ return Collections.unmodifiableList(requestList);
}
}
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
index bc0a3a8..e963a0d 100644
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
@@ -465,7 +465,7 @@
}
@Override
- public int endConfigure() {
+ public int endConfigure(boolean isConstrainedHighSpeed) {
if (DEBUG) {
Log.d(TAG, "endConfigure called.");
}
diff --git a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
index 098c2d8..cc9d496 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
@@ -82,6 +82,7 @@
private static final int GRALLOC_USAGE_SW_READ_OFTEN = 0x00000003;
private static final int GRALLOC_USAGE_HW_TEXTURE = 0x00000100;
private static final int GRALLOC_USAGE_HW_COMPOSER = 0x00000800;
+ private static final int GRALLOC_USAGE_HW_RENDER = 0x00000200;
private static final int GRALLOC_USAGE_HW_VIDEO_ENCODER = 0x00010000;
public static final int MAX_DIMEN_FOR_ROUNDING = 1080; // maximum allowed width for rounding
@@ -549,6 +550,42 @@
return flexibleConsumer;
}
+ public static boolean isPreviewConsumer(Surface output) {
+ int usageFlags = detectSurfaceUsageFlags(output);
+ int disallowedFlags = GRALLOC_USAGE_HW_VIDEO_ENCODER | GRALLOC_USAGE_RENDERSCRIPT |
+ GRALLOC_USAGE_SW_READ_OFTEN;
+ int allowedFlags = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_COMPOSER |
+ GRALLOC_USAGE_HW_RENDER;
+ boolean previewConsumer = ((usageFlags & disallowedFlags) == 0 &&
+ (usageFlags & allowedFlags) != 0);
+ int surfaceFormat = ImageFormat.UNKNOWN;
+ try {
+ surfaceFormat = detectSurfaceType(output);
+ } catch(BufferQueueAbandonedException e) {
+ throw new IllegalArgumentException("Surface was abandoned", e);
+ }
+
+ return previewConsumer && (surfaceFormat == ImageFormat.PRIVATE);
+ }
+
+ public static boolean isVideoEncoderConsumer(Surface output) {
+ int usageFlags = detectSurfaceUsageFlags(output);
+ int disallowedFlags = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_COMPOSER |
+ GRALLOC_USAGE_RENDERSCRIPT | GRALLOC_USAGE_SW_READ_OFTEN;
+ int allowedFlags = GRALLOC_USAGE_HW_VIDEO_ENCODER;
+ boolean videoEncoderConsumer = ((usageFlags & disallowedFlags) == 0 &&
+ (usageFlags & allowedFlags) != 0);
+
+ int surfaceFormat = ImageFormat.UNKNOWN;
+ try {
+ surfaceFormat = detectSurfaceType(output);
+ } catch(BufferQueueAbandonedException e) {
+ throw new IllegalArgumentException("Surface was abandoned", e);
+ }
+
+ return videoEncoderConsumer && (surfaceFormat == ImageFormat.PRIVATE);
+ }
+
/**
* Query the surface for its currently configured usage flags
*/
diff --git a/core/java/android/hardware/camera2/utils/SurfaceUtils.java b/core/java/android/hardware/camera2/utils/SurfaceUtils.java
new file mode 100644
index 0000000..79d82a8
--- /dev/null
+++ b/core/java/android/hardware/camera2/utils/SurfaceUtils.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 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.hardware.camera2.utils;
+
+import android.hardware.camera2.legacy.LegacyCameraDevice;
+import android.hardware.camera2.legacy.LegacyExceptionUtils.BufferQueueAbandonedException;
+import android.util.Size;
+import android.view.Surface;
+
+/**
+ * Various Surface utilities.
+ */
+public class SurfaceUtils {
+
+ /**
+ * Check if a surface is for preview consumer.
+ *
+ * @param surface The surface to be checked.
+ * @return true if the surface is for preview consumer, false otherwise.
+ */
+ public static boolean isSurfaceForPreview(Surface surface) {
+ return LegacyCameraDevice.isPreviewConsumer(surface);
+ }
+
+ /**
+ * Check if the surface is for hardware video encoder consumer.
+ *
+ * @param surface The surface to be checked.
+ * @return true if the surface is for hardware video encoder consumer, false otherwise.
+ */
+ public static boolean isSurfaceForHwVideoEncoder(Surface surface) {
+ return LegacyCameraDevice.isVideoEncoderConsumer(surface);
+ }
+
+ /**
+ * Get the Surface size.
+ *
+ * @param surface The surface to be queried for size.
+ * @return Size of the surface.
+ *
+ * @throw IllegalArgumentException if the surface is already abandoned.
+ */
+ public static Size getSurfaceSize(Surface surface) {
+ try {
+ return LegacyCameraDevice.getSurfaceSize(surface);
+ } catch (BufferQueueAbandonedException e) {
+ throw new IllegalArgumentException("Surface was abandoned", e);
+ }
+ }
+
+ /**
+ * Get the Surface format.
+ *
+ * @param surface The surface to be queried for format.
+ * @return format of the surface.
+ *
+ * @throw IllegalArgumentException if the surface is already abandoned.
+ */
+ public static int getSurfaceFormat(Surface surface) {
+ try {
+ return LegacyCameraDevice.detectSurfaceType(surface);
+ } catch (BufferQueueAbandonedException e) {
+ throw new IllegalArgumentException("Surface was abandoned", e);
+ }
+ }
+}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index a0d1930..0001860 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -694,9 +694,6 @@
*/
private boolean mForceTranscriptScroll;
- private int mGlowPaddingLeft;
- private int mGlowPaddingRight;
-
/**
* Used for interacting with list items from an accessibility service.
*/
@@ -3489,17 +3486,14 @@
if (!mEdgeGlowBottom.isFinished()) {
mEdgeGlowBottom.onRelease();
}
- invalidate(0, 0, getWidth(),
- mEdgeGlowTop.getMaxHeight() + getPaddingTop());
+ invalidateTopGlow();
} else if (incrementalDeltaY < 0) {
mEdgeGlowBottom.onPull((float) overscroll / getHeight(),
1.f - (float) x / getWidth());
if (!mEdgeGlowTop.isFinished()) {
mEdgeGlowTop.onRelease();
}
- invalidate(0, getHeight() - getPaddingBottom() -
- mEdgeGlowBottom.getMaxHeight(), getWidth(),
- getHeight());
+ invalidateBottomGlow();
}
}
}
@@ -3539,17 +3533,14 @@
if (!mEdgeGlowBottom.isFinished()) {
mEdgeGlowBottom.onRelease();
}
- invalidate(0, 0, getWidth(),
- mEdgeGlowTop.getMaxHeight() + getPaddingTop());
+ invalidateTopGlow();
} else if (rawDeltaY < 0) {
mEdgeGlowBottom.onPull((float) overScrollDistance / getHeight(),
1.f - (float) x / getWidth());
if (!mEdgeGlowTop.isFinished()) {
mEdgeGlowTop.onRelease();
}
- invalidate(0, getHeight() - getPaddingBottom() -
- mEdgeGlowBottom.getMaxHeight(), getWidth(),
- getHeight());
+ invalidateBottomGlow();
}
}
}
@@ -3581,6 +3572,28 @@
}
}
+ private void invalidateTopGlow() {
+ if (mEdgeGlowTop == null) {
+ return;
+ }
+ final boolean clipToPadding = getClipToPadding();
+ final int top = clipToPadding ? mPaddingTop : 0;
+ final int left = clipToPadding ? mPaddingLeft : 0;
+ final int right = clipToPadding ? getWidth() - mPaddingRight : getWidth();
+ invalidate(left, top, right, top + mEdgeGlowTop.getMaxHeight());
+ }
+
+ private void invalidateBottomGlow() {
+ if (mEdgeGlowBottom == null) {
+ return;
+ }
+ final boolean clipToPadding = getClipToPadding();
+ final int bottom = clipToPadding ? getHeight() - mPaddingBottom : getHeight();
+ final int left = clipToPadding ? mPaddingLeft : 0;
+ final int right = clipToPadding ? getWidth() - mPaddingRight : getWidth();
+ invalidate(left, bottom - mEdgeGlowBottom.getMaxHeight(), right, bottom);
+ }
+
@Override
public void onTouchModeChanged(boolean isInTouchMode) {
if (isInTouchMode) {
@@ -4142,47 +4155,53 @@
super.draw(canvas);
if (mEdgeGlowTop != null) {
final int scrollY = mScrollY;
+ final boolean clipToPadding = getClipToPadding();
+ final int width;
+ final int height;
+ final int translateX;
+ final int translateY;
+
+ if (clipToPadding) {
+ width = getWidth() - mPaddingLeft - mPaddingRight;
+ height = getHeight() - mPaddingTop - mPaddingBottom;
+ translateX = mPaddingLeft;
+ translateY = mPaddingTop;
+ } else {
+ width = getWidth();
+ height = getHeight();
+ translateX = 0;
+ translateY = 0;
+ }
if (!mEdgeGlowTop.isFinished()) {
final int restoreCount = canvas.save();
- final int width = getWidth();
-
- int edgeY = Math.min(0, scrollY + mFirstPositionDistanceGuess);
- canvas.translate(0, edgeY);
- mEdgeGlowTop.setSize(width, getHeight());
+ canvas.clipRect(translateX, translateY,
+ translateX + width ,translateY + mEdgeGlowTop.getMaxHeight());
+ final int edgeY = Math.min(0, scrollY + mFirstPositionDistanceGuess) + translateY;
+ canvas.translate(translateX, edgeY);
+ mEdgeGlowTop.setSize(width, height);
if (mEdgeGlowTop.draw(canvas)) {
- invalidate(0, 0, getWidth(),
- mEdgeGlowTop.getMaxHeight() + getPaddingTop());
+ invalidateTopGlow();
}
canvas.restoreToCount(restoreCount);
}
if (!mEdgeGlowBottom.isFinished()) {
final int restoreCount = canvas.save();
- final int width = getWidth();
- final int height = getHeight();
-
- int edgeX = -width;
- int edgeY = Math.max(height, scrollY + mLastPositionDistanceGuess);
+ canvas.clipRect(translateX, translateY + height - mEdgeGlowBottom.getMaxHeight(),
+ translateX + width, translateY + height);
+ final int edgeX = -width + translateX;
+ final int edgeY = Math.max(getHeight(), scrollY + mLastPositionDistanceGuess)
+ - (clipToPadding ? mPaddingBottom : 0);
canvas.translate(edgeX, edgeY);
canvas.rotate(180, width, 0);
mEdgeGlowBottom.setSize(width, height);
if (mEdgeGlowBottom.draw(canvas)) {
- invalidate(0, getHeight() - getPaddingBottom() -
- mEdgeGlowBottom.getMaxHeight(), getWidth(),
- getHeight());
+ invalidateBottomGlow();
}
canvas.restoreToCount(restoreCount);
}
}
}
- /**
- * @hide
- */
- public void setOverScrollEffectPadding(int leftPadding, int rightPadding) {
- mGlowPaddingLeft = leftPadding;
- mGlowPaddingRight = rightPadding;
- }
-
private void initOrResetVelocityTracker() {
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 4c98abf..0c4b1e1 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -1756,6 +1756,12 @@
if (!checkFieldAndSelectCurrentWord()) {
return false;
}
+
+ // Avoid dismissing the selection if it exists.
+ mPreserveDetachedSelection = true;
+ stopTextActionMode();
+ mPreserveDetachedSelection = false;
+
getSelectionController().enterDrag();
return true;
}
@@ -3129,7 +3135,9 @@
Callback customCallback = getCustomCallback();
if (customCallback != null) {
if (!customCallback.onCreateActionMode(mode, menu)) {
- // The custom mode can choose to cancel the action mode
+ // The custom mode can choose to cancel the action mode, dismiss selection.
+ Selection.setSelection((Spannable) mTextView.getText(),
+ mTextView.getSelectionEnd());
return false;
}
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 6872caa..b0411c9 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -9072,9 +9072,16 @@
switch (id) {
case ID_SELECT_ALL:
- // This does not enter text selection mode. Text is highlighted, so that it can be
- // bulk edited, like selectAllOnFocus does. Returns true even if text is empty.
+ // This starts an action mode if triggered from another action mode. Text is
+ // highlighted, so that it can be bulk edited, like selectAllOnFocus does. Returns
+ // true even if text is empty.
+ boolean shouldRestartActionMode =
+ mEditor != null && mEditor.mTextActionMode != null;
+ stopTextActionMode();
selectAllText();
+ if (shouldRestartActionMode) {
+ mEditor.startSelectionActionMode();
+ }
return true;
case ID_UNDO:
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 40fee2c..faf926c 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -183,6 +183,7 @@
external/pdfium/core/include/fpdfapi \
external/pdfium/core/include/fpdfdoc \
external/pdfium/fpdfsdk/include \
+ external/pdfium/public \
external/skia/src/core \
external/skia/src/effects \
external/skia/src/images \
diff --git a/core/jni/android/graphics/pdf/PdfEditor.cpp b/core/jni/android/graphics/pdf/PdfEditor.cpp
index 84434ae..52b69e0 100644
--- a/core/jni/android/graphics/pdf/PdfEditor.cpp
+++ b/core/jni/android/graphics/pdf/PdfEditor.cpp
@@ -20,8 +20,8 @@
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor"
#include "fpdfview.h"
-#include "fpdfedit.h"
-#include "fpdfsave.h"
+#include "fpdf_edit.h"
+#include "fpdf_save.h"
#include "fsdk_rendercontext.h"
#include "fpdf_transformpage.h"
#pragma GCC diagnostic pop
@@ -58,7 +58,7 @@
static void initializeLibraryIfNeeded() {
Mutex::Autolock _l(sLock);
if (sUnmatchedInitRequestCount == 0) {
- FPDF_InitLibrary(NULL);
+ FPDF_InitLibrary();
}
sUnmatchedInitRequestCount++;
}
diff --git a/core/jni/android/graphics/pdf/PdfRenderer.cpp b/core/jni/android/graphics/pdf/PdfRenderer.cpp
index 876bea4..006eef8 100644
--- a/core/jni/android/graphics/pdf/PdfRenderer.cpp
+++ b/core/jni/android/graphics/pdf/PdfRenderer.cpp
@@ -50,7 +50,7 @@
static void initializeLibraryIfNeeded() {
Mutex::Autolock _l(sLock);
if (sUnmatchedInitRequestCount == 0) {
- FPDF_InitLibrary(NULL);
+ FPDF_InitLibrary();
}
sUnmatchedInitRequestCount++;
}
@@ -165,12 +165,12 @@
// and FPDF_ANNOT flags. To add support for that refer to FPDF_RenderPage_Retail
// in fpdfview.cpp
- CRenderContext* pContext = FX_NEW CRenderContext;
+ CRenderContext* pContext = new CRenderContext;
CPDF_Page* pPage = (CPDF_Page*) page;
pPage->SetPrivateData((void*) 1, pContext, DropContext);
- CFX_FxgeDevice* fxgeDevice = FX_NEW CFX_FxgeDevice;
+ CFX_FxgeDevice* fxgeDevice = new CFX_FxgeDevice;
pContext->m_pDevice = fxgeDevice;
// Reverse the bytes (last argument TRUE) since the Android
@@ -180,7 +180,7 @@
CPDF_RenderOptions* renderOptions = pContext->m_pOptions;
if (!renderOptions) {
- renderOptions = FX_NEW CPDF_RenderOptions;
+ renderOptions = new CPDF_RenderOptions;
pContext->m_pOptions = renderOptions;
}
@@ -205,7 +205,7 @@
clip.bottom = destBottom;
fxgeDevice->SetClip_Rect(&clip);
- CPDF_RenderContext* pageContext = FX_NEW CPDF_RenderContext;
+ CPDF_RenderContext* pageContext = new CPDF_RenderContext;
pContext->m_pContext = pageContext;
pageContext->Create(pPage);
@@ -232,7 +232,7 @@
}
pageContext->AppendObjectList(pPage, &matrix);
- pContext->m_pRenderer = FX_NEW CPDF_ProgressiveRenderer;
+ pContext->m_pRenderer = new CPDF_ProgressiveRenderer;
pContext->m_pRenderer->Start(pageContext, fxgeDevice, renderOptions, NULL);
fxgeDevice->RestoreState();
diff --git a/docs/html/images/systrace/display-rhythm.png b/docs/html/images/systrace/display-rhythm.png
deleted file mode 100644
index a249161..0000000
--- a/docs/html/images/systrace/display-rhythm.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/images/systrace/frame-selected-alert-tab.png b/docs/html/images/systrace/frame-selected-alert-tab.png
new file mode 100644
index 0000000..e4b666c
--- /dev/null
+++ b/docs/html/images/systrace/frame-selected-alert-tab.png
Binary files differ
diff --git a/docs/html/images/systrace/frame-selected.png b/docs/html/images/systrace/frame-selected.png
new file mode 100644
index 0000000..ee8e11f
--- /dev/null
+++ b/docs/html/images/systrace/frame-selected.png
Binary files differ
diff --git a/docs/html/images/systrace/frame-unselected.png b/docs/html/images/systrace/frame-unselected.png
new file mode 100644
index 0000000..abc93d4
--- /dev/null
+++ b/docs/html/images/systrace/frame-unselected.png
Binary files differ
diff --git a/docs/html/images/systrace/overview.png b/docs/html/images/systrace/overview.png
new file mode 100644
index 0000000..bd7f549
--- /dev/null
+++ b/docs/html/images/systrace/overview.png
Binary files differ
diff --git a/docs/html/images/systrace/process-rhythm.png b/docs/html/images/systrace/process-rhythm.png
deleted file mode 100644
index 7498cc7..0000000
--- a/docs/html/images/systrace/process-rhythm.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/images/systrace/report.png b/docs/html/images/systrace/report.png
deleted file mode 100644
index a642960..0000000
--- a/docs/html/images/systrace/report.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/preview/testing/performance.jd b/docs/html/preview/testing/performance.jd
index 003b619..abb41c4 100644
--- a/docs/html/preview/testing/performance.jd
+++ b/docs/html/preview/testing/performance.jd
@@ -158,6 +158,15 @@
</ul>
</li>
+ <li>INTENDED_VSYNC
+ <ul>
+ <li>The intended start point for the frame. If this value is different from VSYNC, there
+ was work occurring on the UI thread that prevented it from responding to the vsync signal
+ in a timely fashion.
+ </li>
+ </ul>
+ </li>
+
<li>VSYNC
<ul>
<li>The time value that was used in all the vsync listeners and drawing for the frame
@@ -172,16 +181,6 @@
</ul>
</li>
-
- <li>INTENDED_VSYNC
- <ul>
- <li>The intended start point for the frame. If this value is different from VSYNC, there
- was work occurring on the UI thread that prevented it from responding to the vsync signal
- in a timely fashion.
- </li>
- </ul>
- </li>
-
<li>OLDEST_INPUT_EVENT
<ul>
<li>The timestamp of the oldest input event in the input queue, or Long.MAX_VALUE if
diff --git a/docs/html/tools/data-binding/guide.jd b/docs/html/tools/data-binding/guide.jd
index ec16c6b..7c4c15a 100644
--- a/docs/html/tools/data-binding/guide.jd
+++ b/docs/html/tools/data-binding/guide.jd
@@ -141,7 +141,8 @@
— it's a support library, so you can use it with all Android platform
versions back to <strong>Android 2.1</strong> (API level 7+).</p>
-<p>Android Studio <strong>1.3.0-beta1</strong> or higher is required.</p>
+<p>To use data binding, Android Plugin for Gradle <strong>1.3.0-beta1</strong>
+or higher is required.</p>
<h4>Beta release</h4>
@@ -156,7 +157,7 @@
so use it at your own risk. That said, we do want your feedback! Please
let us know what is or isn’t working for you using the <a
href="https://code.google.com/p/android-developer-preview/">issue
- tracker</a>.
+ tracker</a>.
</li>
<li>
The Data Binding library beta release is subject to significant changes,
@@ -174,7 +175,7 @@
Further Android Studio support will come in the future.
</li>
<li>
- By using the Data Binding library beta release, you acknowledge these
+ By using the Data Binding library beta release, you acknowledge these
caveats.</li>
</ul>
</div>
@@ -186,16 +187,13 @@
<p>To get started with Data Binding, download the library from the Support
repository in the Android SDK manager. </p>
-<p>Make sure you are using a compatible version of Android Studio.
-The Data Binding plugin for Android Studio requires Android Studio <strong>1.3.0-beta1
-or higher</strong>.</p>
-<h2 id="build_environment">
- Build Environment
-</h2>
+<p>The Data Binding plugin requires Android Plugin for Gradle <strong>1.3.0-beta1
+or higher</strong>, so update your build dependencies (in
+<code>build.gradle</code>) as needed.</p>
-<p>To get started with Data Binding, download the library from the Support repository in the Android SDK manager. </p>
-
-<p>Make sure you are using a <strong>compatible version of Android Studio</strong>. The Data Binding plugin for Android Studio requires <strong>Android 1.3.0-beta1 or higher</strong>.</p>
+<p>Also, make sure you are using a compatible version of Android Studio.
+<strong>Android Studio 1.3</strong> adds the code-completion and layout-preview
+support for data binding.</p>
<p>
<strong>Setting Up Work Environment:</strong>
@@ -203,12 +201,12 @@
<p>
To set up your application to use data binding, add data binding to the class
- path of your build gradle file, right below "android".
+ path of your <code>build.gradle</code> file, right below "android".
</p>
<pre>
dependencies {
- classpath <strong>"com.android.tools.build:gradle:1.2.3"
+ classpath <strong>"com.android.tools.build:gradle:1.3.0-beta1"
</strong>classpath <strong>"com.android.databinding:dataBinder:</strong>1.0-rc0"
}
}
diff --git a/docs/html/tools/debugging/systrace.jd b/docs/html/tools/debugging/systrace.jd
index 6472152..5d2cd89 100644
--- a/docs/html/tools/debugging/systrace.jd
+++ b/docs/html/tools/debugging/systrace.jd
@@ -1,4 +1,4 @@
-page.title=Analyzing Display and Performance
+page.title=Analyzing UI Performance with Systrace
page.tags=systrace,speed
parent.title=Debugging
parent.link=index.html
@@ -8,22 +8,15 @@
<div id="qv">
<h2>In this document</h2>
<ol>
- <li><a href="#overview">Overview</a>
- </li>
- <li><a href="#generate">Generating Traces</a>
+ <li><a href="#overview">Overview</a></li>
+ <li><a href="#generate">Generating a Trace</a></li>
+ <li><a href="#analysis">Analyzing a Trace</a>
<ol>
- <li><a href="#limit-trace">Limiting trace data</a></li>
- <li><a href="#running-4.3">Tracing on Android 4.3 and higher</a>
- <li><a href="#running-4.2">Tracing on Android 4.2 and lower</a></li>
+ <li><a href="#frames">Inspecting Frames</a></li>
+ <li><a href="#alerts">Investigating Alerts</a></li>
</ol>
</li>
<li><a href="#app-trace">Tracing Application Code</a></li>
- <li><a href="#analysis">Analyzing Traces</a>
- <ol>
- <li><a href="#long-processes">Long running processes</a></li>
- <li><a href="#display-interupts">Interruptions in display execution</a></li>
- </ol>
- </li>
</ol>
<h2>See also</h2>
<ol>
@@ -32,72 +25,55 @@
</div>
</div>
-<p>After building features, eliminating bugs, and cleaning up your code, you should spend some
- time looking at the performance of your application. The speed and smoothness with which your
- application draws pixels and performs operations has an significant impact on your users'
- experience.</p>
+<p>While developing your application, you should check that user interactions are buttery smooth,
+running at a consistent 60 frames per second. If something goes wrong, and a frame gets dropped, the
+first step in fixing the problem is understanding what the system is doing.</p>
-<p>Android applications operate within a shared resource environment, and the performance of
- your application can be impacted by how efficiently it interacts with those resources in
- the larger system. Applications also operate in a multithreaded environment, competing with other
- threaded processes for resources, which can cause performance problems that are hard to diagnose.
-</p>
-
-<p>The Systrace tool allows you to collect and review code execution data for your
- application and the Android system. You can use this data to diagnose execution problems and
- improve the performance of your application.</p>
-
+<p>The Systrace tool allows you to collect and inspect timing information across an entire Android
+device, which is called a <em>trace</em>. It shows where time and CPU cycles are being spent,
+displaying what each thread and process is doing at any given time. It also inpects the captured
+tracing information to highlight problems that it observes, from list item recycling to rendering
+content, and provide recommendations about how to fix them. This document explains how to navigate
+the trace files produced by the tool, and use them to analyze the performance of an application's
+user interface (UI).</p>
<h2 id="overview">Overview</h2>
-<p>Systrace helps you analyze how the execution of your application fits into the larger
- Android environment, letting you see system and applications process execution on a common
- timeline. The tool allows you to generate highly detailed, interactive reports from devices
- running Android 4.1 and higher, such as the report in figure 1.</p>
+<p>Systrace helps you analyze how the execution of your application fits into the many running
+systems on an Android device. It puts together system and application thread execution on a common
+timeline. In order to analyze your app with Systrace, you first collect a trace log of your app, and
+the system activity. The generated trace allows you to view highly detailed, interactive reports
+showing everything happening the system for the traced duration.</p>
-<img src="{@docRoot}images/systrace/report.png" alt="Systrace example report" id="figure1" />
+<img src="{@docRoot}images/systrace/overview.png" alt="Systrace example overview" id="figure1" />
<p class="img-caption">
- <strong>Figure 1.</strong> An example Systrace report on 5 seconds of process execution
- for a running application and related Android system processes.
+ <strong>Figure 1.</strong> An example Systrace, showing 5 seconds of scrolling an app when it
+ is not performing well.
</p>
+<p>Figure 1. shows a trace captured while scrolling an app that is not rendering smoothly. By
+default, a zoomed out view of the traced duration is shown. The horizontal axis is time, and trace
+events are grouped by process, and then by thread on the vertical axis.</p>
-<h2 id="generate">Generating Traces</h2>
+<p>The groupings are in the order Kernel, SurfaceFlinger (the android compositor process), followed
+by apps, each labeled by package name. Each app process contains all of the tracing signals from
+each thread it contains, including a hierarchy of high level tracing events based on the enabled
+tracing categories.</p>
+
+
+<h2 id="generate">Generating a Trace</h2>
<p>In order to create a trace of your application, you must perform a few setup steps. First, you
- must have a device running Android 4.1 or higher. Set up the device for
- <a href="{@docRoot}tools/device.html#setting-up">debugging</a>, connect it to your development
- system, and install your application. Some types of trace information, specifically disk activity
- and kernel work queues, require that you have root access to the device. However, most Systrace
- log data only requires that the device be enabled for developer debugging.</p>
+must have a device running Android 4.1 (API 16) or higher. Set up the device
+for <a href="{@docRoot}tools/device.html#setting-up">debugging</a>, connect it to your development
+system, and install your application. Some types of trace information, specifically disk activity
+and kernel work queues, require that you have root access to the device. However, most Systrace log
+data only requires that the device be enabled for developer debugging.</p>
-<p>Systrace traces can be run either from a
- <a href="{@docRoot}tools/help/systrace.html#options">command line</a> or from a
- <a href="{@docRoot}tools/help/systrace.html#gui">graphical user interface</a>. This guide
- focuses on using the command line options.</p>
-
-
-<h3 id="limit-trace">Limiting trace data</h3>
-
-<p>The Systrace tool can generate a potentially huge amount of data from applications
- and system sources. To limit the amount of data the tool collects and make the data more relevant
- to your analysis, use the following options:</p>
-
-<ul>
- <li>Limit the amount of time covered by the trace with the {@code -t, --time} option. The default
- length of a trace is 5 seconds.</li>
- <li>Limit the size of the data collected by the trace with the {@code -b, --buf-size} option.</li>
- <li>Specify what types of processes are traced. The types of processes that can be traced depends
- on the version of Android you are running:
- <ul>
- <li>Android 4.2 and lower devices: Use the {@code --set-tags} option and the {@code --disk},
- {@code --cpu-freq}, {@code --cpu-idle}, {@code --cpu-load} options.</li>
- <li>Android 4.3 and higher devices: Use the {@code --list-categories} option to see what
- categories are available on your test device.</li>
- </ul>
- </li>
-</ul>
-
+<p>Systrace traces can be run either from
+a <a href="{@docRoot}tools/help/systrace.html#options">command line</a> or from a
+<a href="{@docRoot}tools/help/systrace.html#gui">graphical user interface</a>. This guide focuses on
+using the command line options.</p>
<h3 id="running-4.3">Tracing on Android 4.3 and higher</h3>
@@ -116,7 +92,7 @@
</ol>
<p>For more information on the available options for running Systrace, see the
-<a href="{@docRoot}tools/help/systrace.html#options-4.3">Systrace</a> help page.</p>
+<a href="#options-4.3">Systrace</a> help page.</p>
<h3 id="running-4.2">Tracing on Android 4.2 and lower</h3>
@@ -127,9 +103,9 @@
<ul>
<li>General system processes such as graphics, audio and input processes (selected using trace
- <a href="{@docRoot}tools/help/systrace.html#tags">category tags</a>).</li>
+ <a href="#tags">category tags</a>).</li>
<li>Low level system information such as CPU, kernel and disk activity (selected using
- <a href="{@docRoot}tools/help/systrace.html#options">options</a>).</li>
+ <a href="#options">options</a>).</li>
</ul>
<p>To set trace tags for Systrace using the command-line:</p>
@@ -178,14 +154,85 @@
</ol>
<p>For more information on the available options for running Systrace, see the
-<a href="{@docRoot}tools/help/systrace.html#options-pre-4.3">Systrace</a> help page.</p>
+<a href="#options-pre-4.3">Systrace</a> help page.</p>
+
+
+<h2 id="analysis">Analyzing a Trace</h2>
+
+<p>After you have generated a trace, open the output html file using a web browser. This section
+explains how to analyze and interpret the information that the tool produces to find and fix UI
+performance problems.</p>
+
+<h3 id="frames">Inspecting Frames</h3>
+
+<p>Each app that is rendering frames shows a row of frame circles, which are typically colored
+green. Circles that are colored yellow or red, exceeding the 16.6 millisecond run time limit
+required to maintain a stable 60 frames per second. Zoom in using the 'w' key to see the frames of
+your application, and look for long-running frames getting in the way of smoothness.</p>
+
+<p class="note">
+ <strong>Note:</strong> Hit the '?' key, or the button in the top right for help navigating the
+ trace.
+</p>
+
+<img src="{@docRoot}images/systrace/frame-unselected.png" alt="Zoomed in view of a frame" id="figure2" />
+<p class="img-caption">
+ <strong>Figure 2.</strong> Systrace display after zooming in on a long-running frame.
+</p>
+
+<p>Clicking on one such frame highlights it, focusing only on the work done by the system for that
+frame. On devices running Android 5.0 (API level 21) or higher, this work is split between the UI
+Thread and RenderThread. On prior versions, all work in creating a frame is done on the UI
+Thread.</p>
+
+<p>Click on individual components of the frame to see how long they took to run. Some events, such
+as <em>performTraversals</em>, describe what the system is doing in that method when you select
+it. Selecting a frame displays any alerts present in that frame.</p>
+
+<h3 id="alerts">Investigating Alerts</h3>
+
+<p>Systrace does automatic analysis of the events in the trace, and highlights many performance
+problems as alerts, suggesting what to do next.</p>
+
+<img src="{@docRoot}images/systrace/frame-selected.png" alt="Problematic frame selected" id="figure3" />
+<p class="img-caption">
+ <strong>Figure 3.</strong> Selecting the problematic frame, an alert is shown identifying a problem.
+</p>
+
+<p>After you select a slow frame such as the one shown in Figure 3, an alert may be displayed. In
+the case above, it calls out that the primary problem with the frame is too much work being done
+inside {@link android.widget.ListView} recycling and rebinding. There are links to the relevant
+events in the trace, which can be followed to explain more about what the system is doing during
+this time.</p>
+
+<p>If you see too much work being done on the UI thread, as in this case with this
+{@link android.widget.ListView} work, you can
+use <a href="{@docRoot}tools/debugging/debugging-tracing.html">Traceview</a>, the app code profiling
+tool, to investigate exactly what is taking so much time.</p>
+
+<p>Note that you can also find about every alert in the trace by clicking the <em>Alerts</em> tab to
+the far right of the window. Doing so expands the Alerts panel, where you can see every alert that
+the tool discovered in your trace, along with an occurrence count.</p>
+
+<img src="{@docRoot}images/systrace/frame-selected-alert-tab.png" alt="Alert tab shown" id="figure4" />
+<p class="img-caption">
+ <strong>Figure 4.</strong> Clicking the Alert button to the right reveals the alert tab.
+</p>
+
+<p>The Alerts panel helps you see which problems occur in the trace, and how often they contribute
+to jank. Think of the alerts panel as a list of bugs to be fixed, often a tiny change or improvement
+in one area can eliminate an entire class of alerts from your application!</p>
<h2 id="app-trace">Tracing Application Code</h2>
-<p>The Systrace tool can trace the execution of code within your application. In Android
-4.3 (API level 18) and higher, you can use the methods of the {@link android.os.Trace} class to
-add instrumentation to your application code and see the results in a Systrace report.</p>
+<p>The tracing signals defined by the framework do not have visibility into everything your
+application is doing, so you may want to add your own. In Android 4.3 (API level 18) and higher, you
+can use the methods of the {@link android.os.Trace} class to add signals to your code. This
+technique can help you see what work your application's threads are doing at any given time. Tracing
+begin and end events do add overhead while a trace is being captured, a few microseconds each, but
+sprinkling in a few per frame, or per worker thread task can go a long way to adding context to a
+trace of your app.</p>
<p>The following code example shows how to use the {@link android.os.Trace} class to track
execution of an application method, including two nested code blocks within that method.</p>
@@ -212,6 +259,8 @@
}
}
</pre>
+
+<!-- todo: move these two Notes to the android.os.Trace class -->
<p class="note">
<strong>Note:</strong> When you nest trace calls within each other, the
{@link android.os.Trace#endSection} method ends the most recently called
@@ -229,99 +278,10 @@
<p>When using application-level tracing with Systrace, you must specify the package name of your
application in the user interface or specify the {@code -a} or {@code --app=} options on the
command line. For more information, see the
-<a href="{@docRoot}tools/help/systrace.html">Systrace</a> help page.</p>
+<a href="{@docRoot}tools/help/systrace.html">Systrace usage guide</a>.</p>
-<!-- todo: add ndk coverage -->
+<p>You should enable app level tracing when profiling your app, even if you have not added signals
+yourself. Library code can include very useful tracing signals when you enable application-level
+tracing. The {@link android.support.v7.widget.RecyclerView} class is a great example of this,
+providing information about several important stages of work it executes.</p>
-
-<h2 id="analysis">Analyzing Traces</h2>
-
-<p>After you have generated a trace using Systrace, it lists the location of the output
- file and you can open the report using a web browser.
- How you use the trace data depends on the performance issues you are investigating. However,
- this section provides some general instructions on how to analyze a trace.</p>
-
-<p>The reports generated by Systrace are interactive, allowing you to zoom into and out of
- the process execution details. Use the <em>W</em> key to zoom in, the <em>S</em>
- key to zoom out, the <em>A</em> key to pan left and the <em>D</em> key to pan
- right. Select a task in timeline using your mouse to get more information about the task.
- For more information about the using the keyboard navigation shortcuts and navigation, see the
- <a href="{@docRoot}tools/help/systrace.html#viewing-options">Systrace</a> reference
- documentation.</p>
-
-<h3 id="long-processes">Long running processes</h3>
-
-<p>A well-behaved application executes many small operations quickly and with a regular rhythm,
- with individual operations completing within few milliseconds, depending on the device
- and the processes being performed, as shown in figure 2:</p>
-
-<img src="{@docRoot}images/systrace/process-rhythm.png" alt="Systrace exerpt of app processing"
-id="figure2" />
-<p class="img-caption">
- <strong>Figure 2.</strong> Excerpt from a trace of a smoothly running application with a regular
- execution rhythm.
-</p>
-
-<p>The trace excerpt in figure 2 shows a well-behaved application with
- a regular process rhythm (1). The lower section of figure 2 shows a magnified section of
- the trace indicated by the dotted outline, which reveals some irregularity in the process
- execution. In particular, one of the wider task bars, indicated by (2), is taking slightly
- longer (14 milliseconds) than other, similar tasks on this thread, which are averaging between
- 9 and 12 milliseconds to complete. This particular task execution length is likely not noticeable
- to a user, unless it impacts another process with specific timing, such as a screen update.</p>
-
-<p>Long running processes show up as thicker than usual execution bars in a trace. These thicker
- bars can indicate a problem in your application performance. When they show up in your
- trace, zoom in on the process using the
- <a href="{@docRoot}tools/help/systrace.html#viewing-options">keyboard navigation</a> shortcuts to
- identify the task causing the problem, and click on the task to get more information. You should
- also look at other processes running at the same time, looking for a thread in one process that is
- being blocked by another process.</p>
-
-
-<h3 id="display-interupts">Interruptions in display execution</h3>
-
-<p>The Systrace tool is particularly useful in analyzing application display slowness,
- or pauses in animations, because it shows you the execution of your application across multiple
- system processes. With display execution, drawing screen frames with a regular rhythm is essential
- for good performance. Having a regular rhythm for display ensures that animations and motion are
- smooth on screen. If an application drops out of this rhythm, the display can become jerky or slow
- from the users perspective.</p>
-
-<p>If you are analyzing an application for this type of problem, examine the
- <strong>SurfaceFlinger</strong> process in the Systrace report where your application is
- also executing to look for places where it drops out of its regular rhythm.</p>
-
-<img src="{@docRoot}images/systrace/display-rhythm.png" alt="Systrace exerpt of display processing"
-id="figure3" />
-<p class="img-caption">
- <strong>Figure 3.</strong> Excerpt from a trace of an application showing interruptions in
- display processing.
-</p>
-
-<p>The trace excerpt in figure 3 shows an section of a trace that indicates an interruption in the
- device display. The section of the <strong>SurfaceFlinger</strong> process in top excerpt,
- indicated by (1), shows that display frames are being missed. These
- dropped frames are potentially causing the display to stutter or halt. Zooming into this problem
- area in the lower trace, shows that a memory operation (image buffer dequeuing and allocation) in
- the <strong>surfaceflinger</strong> secondary thread is taking a long time (2). This delay
- causes the application to miss the display update window, indicated by the dotted
- line. As the developer of this application, you should investigate other threads in your
- application that may also be trying to allocate memory at the same time or otherwise blocking
- memory allocation with another request or task.</p>
-
-<p>Regular, rhythmic execution of the <strong>SurfaceFlinger</strong> process is essential to smooth
- display of screen content, particularly for animations and motion. Interruptions in the regular
- execution pattern of this thread is not always an indication of a display problem with your
- application. Further testing is required to determine if this is actually a performance problem
- from a user perspective. Being able to identify display execution patterns like the example above
- can help you detect display problems and build a smooth-running, high-performance application.
-</p>
-
-<p class="note">
- <strong>Note:</strong> When using Systrace to analyze display problems, make sure
- you activate the tracing tags for <strong>Graphics</strong> and <strong>Views</strong>.
-</p>
-
-<p>For more information on the command line options and keyboard controls for Systrace,
-see the <a href="{@docRoot}tools/help/systrace.html">Systrace</a> help page.</p>
\ No newline at end of file
diff --git a/docs/html/tools/help/systrace.jd b/docs/html/tools/help/systrace.jd
index 4461da9..2a8e86f 100644
--- a/docs/html/tools/help/systrace.jd
+++ b/docs/html/tools/help/systrace.jd
@@ -13,9 +13,7 @@
<p>The Systrace tool is particularly useful in diagnosing display problems where an
application is slow to draw or stutters while displaying motion or animation. For more information
on how to use Systrace, see <a href="{@docRoot}tools/debugging/systrace.html">Analyzing
- Display and Performance</a>.</p>
-
-
+ UI Performance with Systrace</a>.</p>
<h2 id="requirements">Requirements</h2>
@@ -37,7 +35,7 @@
<a href="{@docRoot}sdk/installing/studio.html">Android Studio</a>,
or the Android <a href="{@docRoot}tools/help/monitor.html">Device Monitor</a>.
-<p>To run the Systrace user interface:</p>
+<p>To run the Systrace capture user interface:</p>
<div class="toggle-content closed">
<p style="margin-top:5px"><a href="#" onclick="return toggleContent(this)">
@@ -100,7 +98,6 @@
</div>
-
<h2 id="options">Command Line Usage</h2>
<p>The Systrace tool has different command line options for devices running Android 4.3 (API
@@ -118,9 +115,9 @@
<h3 id="options-4.3">Android 4.3 and higher options</h3>
-<p>When you use Systrace on devices running Android 4.3 and higher, you must specify at least one
-trace category tag. Here is an example execution run that sets trace tags and generates a trace
-from a connected device.</p>
+<p>When you use Systrace on devices running Android 4.3 and higher, you can omit trace category tags
+to get the defaults, or you may manually specify tags for inclusion. Here is an example execution
+run that sets trace tags and generates a trace from a connected device.</p>
<pre>
$ cd <em>android-sdk</em>/platform-tools/systrace
@@ -220,19 +217,10 @@
<a href="{@docRoot}guide/topics/manifest/manifest-element.html#package">package names</a>.
The apps must contain tracing instrumentation calls from the {@link android.os.Trace} class.
For more information, see <a href="{@docRoot}tools/debugging/systrace.html#app-trace">Analyzing
- Display and Performance</a>.
+ UI Performance with Systrace</a>.
</td>
</tr>
-
-
- <tr>
- <td><code>--link-assets</code></td>
-
- <td>Link to the original CSS or JavaScript resources instead of embedding them in the HTML
- trace report.</td>
- </tr>
-
<tr>
<td><code>--from-file=<<em>FROM_FILE</em>></code></td>
@@ -240,13 +228,6 @@
</tr>
<tr>
- <td><code>--asset-dir=<<em>ASSET_DIR</em>></code></td>
-
- <td>Specify a directory for the trace report assets. This option is useful for maintaining a
- single set of assets for multiple Systrace reports.</td>
- </tr>
-
- <tr>
<td style="white-space:nowrap">
<code>-e <<em>DEVICE_SERIAL</em>><br>
--serial=<<em>DEVICE_SERIAL</em>></code></td>
@@ -375,13 +356,6 @@
</td>
</tr>
- <tr>
- <td><code>--link-assets</code></td>
-
- <td>Link to the original CSS or JS resources instead of embedding them in the HTML trace
- report.</td>
- </tr>
-
</table>
<p>You can set the trace <a href="#tags">tags</a> for Systrace on
@@ -455,16 +429,4 @@
<td>Select the previous event on the currently selected timeline.</td>
</tr>
- <tr>
- <td><strong>Double Click</strong></td>
-
- <td>Zoom into the trace timeline.</td>
- </tr>
-
- <tr>
- <td><strong>Shift+Double Click</strong></td>
-
- <td>Zoom out of the trace timeline.</td>
- </tr>
-
</table>
diff --git a/docs/html/tools/studio/index.jd b/docs/html/tools/studio/index.jd
index e7de000..0113347 100644
--- a/docs/html/tools/studio/index.jd
+++ b/docs/html/tools/studio/index.jd
@@ -83,11 +83,11 @@
<img src="{@docRoot}images/tools/projectview01.png" />
<p class="img-caption"><strong>Figure 1.</strong> Show the Android project view.</p>
<img src="{@docRoot}images/tools/studio-projectview_scripts.png" />
- <p class="img-caption"><strong>Figure 2.</strong> Show project build Files.</p>
+ <p class="img-caption"><strong>Figure 2.</strong> Show project build files.</p>
<p>The <em>Android</em> project view shows all the build files at the top level of the project
hierarchy under <strong>Gradle Scripts</strong>. Each project module appears as a folder at the
-top level of the project hierarchy and contains these three elements at the top level:</p>
+top level of the project hierarchy and contains these four elements at the top level:</p>
<ul>
<li><code>java/</code> - Source files for the module.</li>
@@ -109,10 +109,10 @@
should notice that the project structure appears different than you may be used to in Eclipse. Each
instance of Android Studio contains a project with one or more application modules. Each
application module folder contains the complete source sets for that module, including
-{@code src/main} and {@code src/androidTest} directories, resources, build
+{@code src/main/} and {@code src/androidTest/} directories, resources, build
file and the Android manifest. For the most part, you will need to modify the files under each
-module's {@code src/main} directory for source code updates, the gradle.build file for build
-specification and the files under {@code src/androidTest} directory for test case creation.
+module's {@code src/main/} directory for source code updates, the gradle.build file for build
+specification and the files under {@code src/androidTest/} directory for test case creation.
<p> <img src="{@docRoot}images/tools/studio-project-layout.png" alt="" /></p>
<p> <class="img-caption"><strong>Figure 3.</strong> View Android Studio <em>Project</em>
@@ -131,7 +131,7 @@
</ul>
<p>For example, selecting the <strong>Problems</strong> view of your project displays links to the
-source files containing any recognized coding and syntax errors, such as missing a XML element
+source files containing any recognized coding and syntax errors, such as missing an XML element
closing tag in a layout file.<p>
<p>For more information, see
@@ -199,7 +199,7 @@
</pre>
<p class="note"><strong>Note:</strong> The <em>applicationId</em> is specified only in your
-build.gradle file, and not in the AndroidManifest.xml file.</p>
+{@code build.gradle} file, and not in the AndroidManifest.xml file.</p>
<p>When using build variants, the build system enables you to uniquely identify different
packages for each product flavors and build types. The application ID in the build type is added as
@@ -643,7 +643,7 @@
installed, set the Android Plugin for Gradle proxy settings in the Gradle build file.</p>
<p>For application-specific HTTP proxy settings, set the proxy settings in the
-<strong>build.gradle</strong> file as required for each application module.</p>
+{@code build.gradle} file as required for each application module.</p>
<pre>
apply plugin: 'com.android.application'
diff --git a/docs/html/tools/tools_toc.cs b/docs/html/tools/tools_toc.cs
index 9951330..82515d4 100644
--- a/docs/html/tools/tools_toc.cs
+++ b/docs/html/tools/tools_toc.cs
@@ -130,7 +130,7 @@
<li><a href="<?cs var:toroot ?>tools/debugging/debugging-ui.html"><span class="en">Optimizing your UI</span></a></li>
<li><a href="<?cs var:toroot ?>tools/debugging/debugging-tracing.html"><span class="en">Profiling with Traceview and dmtracedump</span></a></li>
<li><a href="<?cs var:toroot ?>tools/debugging/annotations.html"><span class="en">Improving Code Inspection with Annotations</span></a></li>
- <li><a href="<?cs var:toroot ?>tools/debugging/systrace.html"><span class="en">Analyzing Display and Performance</span></a></li>
+ <li><a href="<?cs var:toroot ?>tools/debugging/systrace.html"><span class="en">Analyzing UI Performance</span></a></li>
<li><a href="<?cs var:toroot ?>tools/debugging/debugging-memory.html">Investigating Your RAM Usage</a></li>
<li><a href="<?cs var:toroot ?>tools/debugging/debugging-devtools.html"><span class="en">Using the Dev Tools App</span></a></li>
</ul>
diff --git a/docs/html/training/articles/perf-tips.jd b/docs/html/training/articles/perf-tips.jd
index e9df51b..4a3184c 100644
--- a/docs/html/training/articles/perf-tips.jd
+++ b/docs/html/training/articles/perf-tips.jd
@@ -427,7 +427,7 @@
<ul>
<li><a href="{@docRoot}tools/debugging/debugging-tracing.html">Profiling with
Traceview and dmtracedump</a></li>
- <li><a href="{@docRoot}tools/debugging/systrace.html">Analysing Display and Performance
+ <li><a href="{@docRoot}tools/debugging/systrace.html">Analyzing UI Performance
with Systrace</a></li>
</ul>
diff --git a/docs/html/tv/adt-1/index.jd b/docs/html/tv/adt-1/index.jd
index 82760ed..4f62796 100644
--- a/docs/html/tv/adt-1/index.jd
+++ b/docs/html/tv/adt-1/index.jd
@@ -274,14 +274,6 @@
starts the multi-color cycle, release the small, round button, and ADT-1 boots up. If you release
the button while the LED is flashing red, the device will be in Fastboot mode.</p>
-<p>
- <strong>There is a hardware problem with my ADT-1. How do I return it?</strong>
-</p>
-<p>You can request a return of the device using the
- <a href="https://support.google.com/googleplay/android-developer/contact/adt_rma">return
- merchandise authorization form</a>.
-</p>
-
<h2 id="emote">Android TV Remote Control App</h2>
diff --git a/packages/DocumentsUI/res/color/item_root_icon.xml b/packages/DocumentsUI/res/color/item_root_icon.xml
index 1374e61..0aa2c13 100644
--- a/packages/DocumentsUI/res/color/item_root_icon.xml
+++ b/packages/DocumentsUI/res/color/item_root_icon.xml
@@ -15,7 +15,5 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_focused="true" android:state_activated="true" android:color="@*android:color/primary_text_default_material_dark" />
- <item android:state_focused="false" android:state_activated="true" android:color="@*android:color/primary_text_default_material_dark" />
<item android:color="@*android:color/secondary_text_material_light" />
</selector>
diff --git a/packages/DocumentsUI/res/color/item_root_primary_text.xml b/packages/DocumentsUI/res/color/item_root_primary_text.xml
index 7e703fa..551245f 100644
--- a/packages/DocumentsUI/res/color/item_root_primary_text.xml
+++ b/packages/DocumentsUI/res/color/item_root_primary_text.xml
@@ -15,8 +15,8 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_focused="true" android:state_activated="true" android:color="@*android:color/primary_text_default_material_dark" />
- <item android:state_focused="false" android:state_activated="true" android:color="@*android:color/primary_text_default_material_dark" />
+ <item android:state_focused="true" android:state_activated="true" android:color="?android:colorAccent" />
+ <item android:state_focused="false" android:state_activated="true" android:color="?android:colorAccent" />
<item android:state_enabled="false" android:alpha="@*android:dimen/disabled_alpha_material_light" android:color="@*android:color/primary_text_default_material_light" />
<item android:color="@*android:color/primary_text_default_material_light" />
</selector>
diff --git a/packages/DocumentsUI/res/drawable/item_root_background.xml b/packages/DocumentsUI/res/drawable/item_root_background.xml
index 1b3f44a..93c965f 100644
--- a/packages/DocumentsUI/res/drawable/item_root_background.xml
+++ b/packages/DocumentsUI/res/drawable/item_root_background.xml
@@ -16,10 +16,10 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="true" android:state_activated="true">
- <color android:color="?android:attr/colorAccent" />
+ <color android:color="@color/material_grey_300" />
</item>
<item android:state_focused="false" android:state_activated="true">
- <color android:color="?android:attr/colorAccent" />
+ <color android:color="@color/material_grey_300" />
</item>
<item android:drawable="@android:color/transparent" />
</selector>
diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk
index 8c128d3..d311cd9 100644
--- a/tools/aapt2/Android.mk
+++ b/tools/aapt2/Android.mk
@@ -36,6 +36,7 @@
Linker.cpp \
Locale.cpp \
Logger.cpp \
+ ManifestMerger.cpp \
ManifestParser.cpp \
ManifestValidator.cpp \
Png.cpp \
@@ -65,6 +66,7 @@
JavaClassGenerator_test.cpp \
Linker_test.cpp \
Locale_test.cpp \
+ ManifestMerger_test.cpp \
ManifestParser_test.cpp \
Maybe_test.cpp \
NameMangler_test.cpp \
@@ -102,7 +104,7 @@
endif
cFlags := -Wall -Werror -Wno-unused-parameter -UNDEBUG
-cppFlags := -std=c++11 -Wno-missing-field-initializers
+cppFlags := -std=c++11 -Wno-missing-field-initializers -Wno-unused-private-field
# ==========================================================
# Build the host static library: libaapt2
diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp
index 84957b4..de2dafc 100644
--- a/tools/aapt2/Main.cpp
+++ b/tools/aapt2/Main.cpp
@@ -23,6 +23,7 @@
#include "Flag.h"
#include "JavaClassGenerator.h"
#include "Linker.h"
+#include "ManifestMerger.h"
#include "ManifestParser.h"
#include "ManifestValidator.h"
#include "NameMangler.h"
@@ -56,6 +57,20 @@
using namespace aapt;
/**
+ * Used with smart pointers to free malloc'ed memory.
+ */
+struct DeleteMalloc {
+ void operator()(void* ptr) {
+ free(ptr);
+ }
+};
+
+struct StaticLibraryData {
+ Source source;
+ std::unique_ptr<ZipFile> apk;
+};
+
+/**
* Collect files from 'root', filtering out any files that do not
* match the FileFilter 'filter'.
*/
@@ -493,6 +508,7 @@
}
bool compileManifest(const AaptOptions& options, const std::shared_ptr<IResolver>& resolver,
+ const std::map<std::shared_ptr<ResourceTable>, StaticLibraryData>& libApks,
const android::ResTable& table, ZipFile* outApk) {
if (options.verbose) {
Logger::note(options.manifest) << "compiling AndroidManifest.xml." << std::endl;
@@ -510,9 +526,40 @@
return false;
}
+ ManifestMerger merger({});
+ if (!merger.setAppManifest(options.manifest, options.appInfo.package, std::move(root))) {
+ return false;
+ }
+
+ for (const auto& entry : libApks) {
+ ZipFile* libApk = entry.second.apk.get();
+ const std::u16string& libPackage = entry.first->getPackage();
+ const Source& libSource = entry.second.source;
+
+ ZipEntry* zipEntry = libApk->getEntryByName("AndroidManifest.xml");
+ if (!zipEntry) {
+ continue;
+ }
+
+ std::unique_ptr<void, DeleteMalloc> uncompressedData = std::unique_ptr<void, DeleteMalloc>(
+ libApk->uncompress(zipEntry));
+ assert(uncompressedData);
+
+ SourceLogger logger(libSource);
+ std::unique_ptr<xml::Node> libRoot = xml::inflate(uncompressedData.get(),
+ zipEntry->getUncompressedLen(), &logger);
+ if (!libRoot) {
+ return false;
+ }
+
+ if (!merger.mergeLibraryManifest(libSource, libPackage, std::move(libRoot))) {
+ return false;
+ }
+ }
+
BigBuffer outBuffer(1024);
- if (!xml::flattenAndLink(options.manifest, root.get(), options.appInfo.package, resolver, {},
- &outBuffer)) {
+ if (!xml::flattenAndLink(options.manifest, merger.getMergedXml(), options.appInfo.package,
+ resolver, {}, &outBuffer)) {
return false;
}
@@ -667,17 +714,6 @@
static constexpr int kOpenFlags = ZipFile::kOpenCreate | ZipFile::kOpenTruncate |
ZipFile::kOpenReadWrite;
-struct DeleteMalloc {
- void operator()(void* ptr) {
- free(ptr);
- }
-};
-
-struct StaticLibraryData {
- Source source;
- std::unique_ptr<ZipFile> apk;
-};
-
bool link(const AaptOptions& options, const std::shared_ptr<ResourceTable>& outTable,
const std::shared_ptr<IResolver>& resolver) {
std::map<std::shared_ptr<ResourceTable>, StaticLibraryData> apkFiles;
@@ -770,7 +806,7 @@
}
android::ResTable binTable;
- if (!compileManifest(options, resolver, binTable, &outApk)) {
+ if (!compileManifest(options, resolver, apkFiles, binTable, &outApk)) {
return false;
}
diff --git a/tools/aapt2/ManifestMerger.cpp b/tools/aapt2/ManifestMerger.cpp
new file mode 100644
index 0000000..71d3424
--- /dev/null
+++ b/tools/aapt2/ManifestMerger.cpp
@@ -0,0 +1,376 @@
+#include "ManifestMerger.h"
+#include "Maybe.h"
+#include "ResourceParser.h"
+#include "Source.h"
+#include "Util.h"
+#include "XmlPullParser.h"
+
+#include <iostream>
+#include <memory>
+#include <set>
+#include <string>
+
+namespace aapt {
+
+constexpr const char16_t* kSchemaAndroid = u"http://schemas.android.com/apk/res/android";
+
+static xml::Element* findManifest(xml::Node* root) {
+ if (!root) {
+ return nullptr;
+ }
+
+ while (root->type == xml::NodeType::kNamespace) {
+ if (root->children.empty()) {
+ break;
+ }
+ root = root->children[0].get();
+ }
+
+ if (root && root->type == xml::NodeType::kElement) {
+ xml::Element* el = static_cast<xml::Element*>(root);
+ if (el->namespaceUri.empty() && el->name == u"manifest") {
+ return el;
+ }
+ }
+ return nullptr;
+}
+
+static xml::Element* findChildWithSameName(xml::Element* parent, xml::Element* src) {
+ xml::Attribute* attrKey = src->findAttribute(kSchemaAndroid, u"name");
+ if (!attrKey) {
+ return nullptr;
+ }
+ return parent->findChildWithAttribute(src->namespaceUri, src->name, attrKey);
+}
+
+static bool attrLess(const xml::Attribute& lhs, const xml::Attribute& rhs) {
+ return std::tie(lhs.namespaceUri, lhs.name, lhs.value)
+ < std::tie(rhs.namespaceUri, rhs.name, rhs.value);
+}
+
+static int compare(xml::Element* lhs, xml::Element* rhs) {
+ int diff = lhs->attributes.size() - rhs->attributes.size();
+ if (diff != 0) {
+ return diff;
+ }
+
+ std::set<xml::Attribute, decltype(&attrLess)> lhsAttrs(&attrLess);
+ lhsAttrs.insert(lhs->attributes.begin(), lhs->attributes.end());
+ for (auto& attr : rhs->attributes) {
+ if (lhsAttrs.erase(attr) == 0) {
+ // The rhs attribute is not in the left.
+ return -1;
+ }
+ }
+
+ if (!lhsAttrs.empty()) {
+ // The lhs has attributes not in the rhs.
+ return 1;
+ }
+ return 0;
+}
+
+ManifestMerger::ManifestMerger(const Options& options) :
+ mOptions(options), mAppLogger({}), mLogger({}) {
+}
+
+bool ManifestMerger::setAppManifest(const Source& source, const std::u16string& package,
+ std::unique_ptr<xml::Node> root) {
+
+ mAppLogger = SourceLogger{ source };
+ mRoot = std::move(root);
+ return true;
+}
+
+bool ManifestMerger::checkEqual(xml::Element* elA, xml::Element* elB) {
+ if (compare(elA, elB) != 0) {
+ mLogger.error(elB->lineNumber)
+ << "library tag '" << elB->name << "' conflicts with app tag."
+ << std::endl;
+ mAppLogger.note(elA->lineNumber)
+ << "app tag '" << elA->name << "' defined here."
+ << std::endl;
+ return false;
+ }
+
+ std::vector<xml::Element*> childrenA = elA->getChildElements();
+ std::vector<xml::Element*> childrenB = elB->getChildElements();
+
+ if (childrenA.size() != childrenB.size()) {
+ mLogger.error(elB->lineNumber)
+ << "library tag '" << elB->name << "' children conflict with app tag."
+ << std::endl;
+ mAppLogger.note(elA->lineNumber)
+ << "app tag '" << elA->name << "' defined here."
+ << std::endl;
+ return false;
+ }
+
+ auto cmp = [](xml::Element* lhs, xml::Element* rhs) -> bool {
+ return compare(lhs, rhs) < 0;
+ };
+
+ std::sort(childrenA.begin(), childrenA.end(), cmp);
+ std::sort(childrenB.begin(), childrenB.end(), cmp);
+
+ for (size_t i = 0; i < childrenA.size(); i++) {
+ if (!checkEqual(childrenA[i], childrenB[i])) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool ManifestMerger::mergeNewOrEqual(xml::Element* parentA, xml::Element* elA, xml::Element* elB) {
+ if (!elA) {
+ parentA->addChild(elB->clone());
+ return true;
+ }
+ return checkEqual(elA, elB);
+}
+
+bool ManifestMerger::mergePreferRequired(xml::Element* parentA, xml::Element* elA,
+ xml::Element* elB) {
+ if (!elA) {
+ parentA->addChild(elB->clone());
+ return true;
+ }
+
+ xml::Attribute* reqA = elA->findAttribute(kSchemaAndroid, u"required");
+ xml::Attribute* reqB = elB->findAttribute(kSchemaAndroid, u"required");
+ bool requiredA = !reqA || (reqA->value != u"false" && reqA->value != u"FALSE");
+ bool requiredB = !reqB || (reqB->value != u"false" && reqB->value != u"FALSE");
+ if (!requiredA && requiredB) {
+ if (reqA) {
+ *reqA = xml::Attribute{ kSchemaAndroid, u"required", u"true" };
+ } else {
+ elA->attributes.push_back(xml::Attribute{ kSchemaAndroid, u"required", u"true" });
+ }
+ }
+ return true;
+}
+
+static int findIntegerValue(xml::Attribute* attr, int defaultValue) {
+ if (attr) {
+ std::unique_ptr<BinaryPrimitive> integer = ResourceParser::tryParseInt(attr->value);
+ if (integer) {
+ return integer->value.data;
+ }
+ }
+ return defaultValue;
+}
+
+bool ManifestMerger::mergeUsesSdk(xml::Element* elA, xml::Element* elB) {
+ bool error = false;
+ xml::Attribute* minAttrA = nullptr;
+ xml::Attribute* minAttrB = nullptr;
+ if (elA) {
+ minAttrA = elA->findAttribute(kSchemaAndroid, u"minSdkVersion");
+ }
+
+ if (elB) {
+ minAttrB = elB->findAttribute(kSchemaAndroid, u"minSdkVersion");
+ }
+
+ int minSdkA = findIntegerValue(minAttrA, 1);
+ int minSdkB = findIntegerValue(minAttrB, 1);
+
+ if (minSdkA < minSdkB) {
+ std::ostream* out;
+ if (minAttrA) {
+ out = &(mAppLogger.error(elA->lineNumber) << "app declares ");
+ } else if (elA) {
+ out = &(mAppLogger.error(elA->lineNumber) << "app has implied ");
+ } else {
+ out = &(mAppLogger.error() << "app has implied ");
+ }
+
+ *out << "minSdkVersion=" << minSdkA << " but library expects a higher SDK version."
+ << std::endl;
+
+ // elB is valid because minSdkB wouldn't be greater than minSdkA if it wasn't.
+ mLogger.note(elB->lineNumber)
+ << "library declares minSdkVersion=" << minSdkB << "."
+ << std::endl;
+ error = true;
+ }
+
+ xml::Attribute* targetAttrA = nullptr;
+ xml::Attribute* targetAttrB = nullptr;
+
+ if (elA) {
+ targetAttrA = elA->findAttribute(kSchemaAndroid, u"targetSdkVersion");
+ }
+
+ if (elB) {
+ targetAttrB = elB->findAttribute(kSchemaAndroid, u"targetSdkVersion");
+ }
+
+ int targetSdkA = findIntegerValue(targetAttrA, minSdkA);
+ int targetSdkB = findIntegerValue(targetAttrB, minSdkB);
+
+ if (targetSdkA < targetSdkB) {
+ std::ostream* out;
+ if (targetAttrA) {
+ out = &(mAppLogger.warn(elA->lineNumber) << "app declares ");
+ } else if (elA) {
+ out = &(mAppLogger.warn(elA->lineNumber) << "app has implied ");
+ } else {
+ out = &(mAppLogger.warn() << "app has implied ");
+ }
+
+ *out << "targetSdkVerion=" << targetSdkA << " but library expects target SDK "
+ << targetSdkB << "." << std::endl;
+
+ mLogger.note(elB->lineNumber)
+ << "library declares targetSdkVersion=" << targetSdkB << "."
+ << std::endl;
+ error = true;
+ }
+ return !error;
+}
+
+bool ManifestMerger::mergeApplication(xml::Element* applicationA, xml::Element* applicationB) {
+ if (!applicationA || !applicationB) {
+ return true;
+ }
+
+ bool error = false;
+
+ // First make sure that the names are identical.
+ xml::Attribute* nameA = applicationA->findAttribute(kSchemaAndroid, u"name");
+ xml::Attribute* nameB = applicationB->findAttribute(kSchemaAndroid, u"name");
+ if (nameB) {
+ if (!nameA) {
+ applicationA->attributes.push_back(*nameB);
+ } else if (nameA->value != nameB->value) {
+ mLogger.error(applicationB->lineNumber)
+ << "conflicting application name '"
+ << nameB->value
+ << "'." << std::endl;
+ mAppLogger.note(applicationA->lineNumber)
+ << "application defines application name '"
+ << nameA->value
+ << "'." << std::endl;
+ error = true;
+ }
+ }
+
+ // Now we descend into the activity/receiver/service/provider tags
+ for (xml::Element* elB : applicationB->getChildElements()) {
+ if (!elB->namespaceUri.empty()) {
+ continue;
+ }
+
+ if (elB->name == u"activity" || elB->name == u"activity-alias"
+ || elB->name == u"service" || elB->name == u"receiver"
+ || elB->name == u"provider" || elB->name == u"meta-data") {
+ xml::Element* elA = findChildWithSameName(applicationA, elB);
+ error |= !mergeNewOrEqual(applicationA, elA, elB);
+ } else if (elB->name == u"uses-library") {
+ xml::Element* elA = findChildWithSameName(applicationA, elB);
+ error |= !mergePreferRequired(applicationA, elA, elB);
+ }
+ }
+ return !error;
+}
+
+bool ManifestMerger::mergeLibraryManifest(const Source& source, const std::u16string& package,
+ std::unique_ptr<xml::Node> libRoot) {
+ mLogger = SourceLogger{ source };
+ xml::Element* manifestA = findManifest(mRoot.get());
+ xml::Element* manifestB = findManifest(libRoot.get());
+ if (!manifestA) {
+ mAppLogger.error() << "missing manifest tag." << std::endl;
+ return false;
+ }
+
+ if (!manifestB) {
+ mLogger.error() << "library missing manifest tag." << std::endl;
+ return false;
+ }
+
+ bool error = false;
+
+ // Do <application> first.
+ xml::Element* applicationA = manifestA->findChild({}, u"application");
+ xml::Element* applicationB = manifestB->findChild({}, u"application");
+ error |= !mergeApplication(applicationA, applicationB);
+
+ // Do <uses-sdk> next.
+ xml::Element* usesSdkA = manifestA->findChild({}, u"uses-sdk");
+ xml::Element* usesSdkB = manifestB->findChild({}, u"uses-sdk");
+ error |= !mergeUsesSdk(usesSdkA, usesSdkB);
+
+ for (xml::Element* elB : manifestB->getChildElements()) {
+ if (!elB->namespaceUri.empty()) {
+ continue;
+ }
+
+ if (elB->name == u"uses-permission" || elB->name == u"permission"
+ || elB->name == u"permission-group" || elB->name == u"permission-tree") {
+ xml::Element* elA = findChildWithSameName(manifestA, elB);
+ error |= !mergeNewOrEqual(manifestA, elA, elB);
+ } else if (elB->name == u"uses-feature") {
+ xml::Element* elA = findChildWithSameName(manifestA, elB);
+ error |= !mergePreferRequired(manifestA, elA, elB);
+ } else if (elB->name == u"uses-configuration" || elB->name == u"supports-screen"
+ || elB->name == u"compatible-screens" || elB->name == u"supports-gl-texture") {
+ xml::Element* elA = findChildWithSameName(manifestA, elB);
+ error |= !checkEqual(elA, elB);
+ }
+ }
+ return !error;
+}
+
+static void printMerged(xml::Node* node, int depth) {
+ std::string indent;
+ for (int i = 0; i < depth; i++) {
+ indent += " ";
+ }
+
+ switch (node->type) {
+ case xml::NodeType::kNamespace:
+ std::cerr << indent << "N: "
+ << "xmlns:" << static_cast<xml::Namespace*>(node)->namespacePrefix
+ << "=\"" << static_cast<xml::Namespace*>(node)->namespaceUri
+ << "\"\n";
+ break;
+
+ case xml::NodeType::kElement:
+ std::cerr << indent << "E: "
+ << static_cast<xml::Element*>(node)->namespaceUri
+ << ":" << static_cast<xml::Element*>(node)->name
+ << "\n";
+ for (const auto& attr : static_cast<xml::Element*>(node)->attributes) {
+ std::cerr << indent << " A: "
+ << attr.namespaceUri
+ << ":" << attr.name
+ << "=\"" << attr.value << "\"\n";
+ }
+ break;
+
+ case xml::NodeType::kText:
+ std::cerr << indent << "T: \"" << static_cast<xml::Text*>(node)->text << "\"\n";
+ break;
+ }
+
+ for (auto& child : node->children) {
+ printMerged(child.get(), depth + 1);
+ }
+}
+
+xml::Node* ManifestMerger::getMergedXml() {
+ return mRoot.get();
+}
+
+bool ManifestMerger::printMerged() {
+ if (!mRoot) {
+ return false;
+ }
+
+ ::aapt::printMerged(mRoot.get(), 0);
+ return true;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/ManifestMerger.h b/tools/aapt2/ManifestMerger.h
new file mode 100644
index 0000000..c6219db
--- /dev/null
+++ b/tools/aapt2/ManifestMerger.h
@@ -0,0 +1,45 @@
+#ifndef AAPT_MANIFEST_MERGER_H
+#define AAPT_MANIFEST_MERGER_H
+
+#include "Logger.h"
+#include "Source.h"
+#include "XmlDom.h"
+
+#include <memory>
+#include <string>
+
+namespace aapt {
+
+class ManifestMerger {
+public:
+ struct Options {
+ };
+
+ ManifestMerger(const Options& options);
+
+ bool setAppManifest(const Source& source, const std::u16string& package,
+ std::unique_ptr<xml::Node> root);
+
+ bool mergeLibraryManifest(const Source& source, const std::u16string& package,
+ std::unique_ptr<xml::Node> libRoot);
+
+ xml::Node* getMergedXml();
+
+ bool printMerged();
+
+private:
+ bool mergeNewOrEqual(xml::Element* parentA, xml::Element* elA, xml::Element* elB);
+ bool mergePreferRequired(xml::Element* parentA, xml::Element* elA, xml::Element* elB);
+ bool checkEqual(xml::Element* elA, xml::Element* elB);
+ bool mergeApplication(xml::Element* applicationA, xml::Element* applicationB);
+ bool mergeUsesSdk(xml::Element* elA, xml::Element* elB);
+
+ Options mOptions;
+ std::unique_ptr<xml::Node> mRoot;
+ SourceLogger mAppLogger;
+ SourceLogger mLogger;
+};
+
+} // namespace aapt
+
+#endif // AAPT_MANIFEST_MERGER_H
diff --git a/tools/aapt2/ManifestMerger_test.cpp b/tools/aapt2/ManifestMerger_test.cpp
new file mode 100644
index 0000000..6838253
--- /dev/null
+++ b/tools/aapt2/ManifestMerger_test.cpp
@@ -0,0 +1,121 @@
+/*
+ * 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 "ManifestMerger.h"
+#include "SourceXmlPullParser.h"
+
+#include <gtest/gtest.h>
+#include <sstream>
+#include <string>
+
+namespace aapt {
+
+constexpr const char* kAppManifest = R"EOF(<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+ <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="21" />
+ <uses-permission android:name="android.permission.INTERNET"/>
+ <uses-feature android:name="android.hardware.GPS" android:required="false" />
+ <application android:name="com.android.library.Application">
+ <activity android:name="com.android.example.MainActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+ <service android:name="com.android.library.Service">
+ <intent-filter>
+ <action android:name="com.android.library.intent.action.SYNC" />
+ </intent-filter>
+ </service>
+ </application>
+</manifest>
+)EOF";
+
+constexpr const char* kLibManifest = R"EOF(<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+ <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="21" />
+ <uses-permission android:name="android.permission.INTERNET" />
+ <uses-feature android:name="android.hardware.GPS" />
+ <uses-permission android:name="android.permission.GPS" />
+ <application android:name="com.android.library.Application">
+ <service android:name="com.android.library.Service">
+ <intent-filter>
+ <action android:name="com.android.library.intent.action.SYNC" />
+ </intent-filter>
+ </service>
+ <provider android:name="com.android.library.DocumentProvider"
+ android:authorities="com.android.library.documents"
+ android:grantUriPermission="true"
+ android:exported="true"
+ android:permission="android.permission.MANAGE_DOCUMENTS"
+ android:enabled="@bool/atLeastKitKat">
+ <intent-filter>
+ <action android:name="android.content.action.DOCUMENTS_PROVIDER" />
+ </intent-filter>
+ </provider>
+ </application>
+</manifest>
+)EOF";
+
+constexpr const char* kBadLibManifest = R"EOF(<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+ <uses-sdk android:minSdkVersion="17" android:targetSdkVersion="22" />
+ <uses-permission android:name="android.permission.INTERNET" />
+ <uses-feature android:name="android.hardware.GPS" />
+ <uses-permission android:name="android.permission.GPS" />
+ <application android:name="com.android.library.Application2">
+ <service android:name="com.android.library.Service">
+ <intent-filter>
+ <action android:name="com.android.library.intent.action.SYNC_ACTION" />
+ </intent-filter>
+ </service>
+ </application>
+</manifest>
+)EOF";
+
+TEST(ManifestMergerTest, MergeManifestsSuccess) {
+ std::stringstream inA(kAppManifest);
+ std::stringstream inB(kLibManifest);
+
+ const Source sourceA = { "AndroidManifest.xml" };
+ const Source sourceB = { "lib.apk/AndroidManifest.xml" };
+ SourceLogger loggerA(sourceA);
+ SourceLogger loggerB(sourceB);
+
+ ManifestMerger merger({});
+ EXPECT_TRUE(merger.setAppManifest(sourceA, u"com.android.example",
+ xml::inflate(&inA, &loggerA)));
+ EXPECT_TRUE(merger.mergeLibraryManifest(sourceB, u"com.android.library",
+ xml::inflate(&inB, &loggerB)));
+}
+
+TEST(ManifestMergerTest, MergeManifestFail) {
+ std::stringstream inA(kAppManifest);
+ std::stringstream inB(kBadLibManifest);
+
+ const Source sourceA = { "AndroidManifest.xml" };
+ const Source sourceB = { "lib.apk/AndroidManifest.xml" };
+ SourceLogger loggerA(sourceA);
+ SourceLogger loggerB(sourceB);
+
+ ManifestMerger merger({});
+ EXPECT_TRUE(merger.setAppManifest(sourceA, u"com.android.example",
+ xml::inflate(&inA, &loggerA)));
+ EXPECT_FALSE(merger.mergeLibraryManifest(sourceB, u"com.android.library",
+ xml::inflate(&inB, &loggerB)));
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/data/lib/AndroidManifest.xml b/tools/aapt2/data/lib/AndroidManifest.xml
index c1612e5..08b468e 100644
--- a/tools/aapt2/data/lib/AndroidManifest.xml
+++ b/tools/aapt2/data/lib/AndroidManifest.xml
@@ -1,3 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.appcompat"/>
+ package="android.appcompat">
+
+ <uses-feature android:name="bloooop" />
+</manifest>