diff options
38 files changed, 1003 insertions, 542 deletions
diff --git a/api/current.txt b/api/current.txt index 35f5f09..529ffe2 100644 --- a/api/current.txt +++ b/api/current.txt @@ -2725,8 +2725,6 @@ package android.app { method public void addContentView(android.view.View, android.view.ViewGroup.LayoutParams); method public void closeContextMenu(); method public void closeOptionsMenu(); - method public void convertFromTranslucent(); - method public void convertToTranslucent(android.app.Activity.TranslucentConversionListener); method public android.app.PendingIntent createPendingResult(int, android.content.Intent, int); method public final deprecated void dismissDialog(int); method public boolean dispatchGenericMotionEvent(android.view.MotionEvent); @@ -2912,10 +2910,6 @@ package android.app { field public static final int RESULT_OK = -1; // 0xffffffff } - public static abstract interface Activity.TranslucentConversionListener { - method public abstract void onTranslucentConversionComplete(boolean); - } - public deprecated class ActivityGroup extends android.app.Activity { ctor public ActivityGroup(); ctor public ActivityGroup(boolean); @@ -10973,6 +10967,8 @@ package android.hardware.camera2 { field public static final int STATISTICS_FACE_DETECT_MODE_FULL = 2; // 0x2 field public static final int STATISTICS_FACE_DETECT_MODE_OFF = 0; // 0x0 field public static final int STATISTICS_FACE_DETECT_MODE_SIMPLE = 1; // 0x1 + field public static final int STATISTICS_LENS_SHADING_MAP_MODE_OFF = 0; // 0x0 + field public static final int STATISTICS_LENS_SHADING_MAP_MODE_ON = 1; // 0x1 field public static final int STATISTICS_SCENE_FLICKER_50HZ = 1; // 0x1 field public static final int STATISTICS_SCENE_FLICKER_60HZ = 2; // 0x2 field public static final int STATISTICS_SCENE_FLICKER_NONE = 0; // 0x0 @@ -11078,6 +11074,7 @@ package android.hardware.camera2 { field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_FRAME_DURATION; field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_SENSITIVITY; field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_FACE_DETECT_MODE; + field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_LENS_SHADING_MAP_MODE; field public static final android.hardware.camera2.CameraMetadata.Key TONEMAP_CURVE_BLUE; field public static final android.hardware.camera2.CameraMetadata.Key TONEMAP_CURVE_GREEN; field public static final android.hardware.camera2.CameraMetadata.Key TONEMAP_CURVE_RED; @@ -30164,6 +30161,7 @@ package android.webkit { method public void clearSslPreferences(); method public deprecated void clearView(); method public android.webkit.WebBackForwardList copyBackForwardList(); + method public android.print.PrintDocumentAdapter createPrintDocumentAdapter(); method public void destroy(); method public void documentHasImages(android.os.Message); method public void evaluateJavascript(java.lang.String, android.webkit.ValueCallback<java.lang.String>); diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 57686a4..a38fbbf 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -4907,6 +4907,8 @@ public class Activity extends ContextThemeWrapper * * @see #convertToTranslucent(TranslucentConversionListener) * @see TranslucentConversionListener + * + * @hide */ public void convertFromTranslucent() { try { @@ -4937,6 +4939,8 @@ public class Activity extends ContextThemeWrapper * * @see #convertFromTranslucent() * @see TranslucentConversionListener + * + * @hide */ public void convertToTranslucent(TranslucentConversionListener callback) { try { @@ -5441,6 +5445,8 @@ public class Activity extends ContextThemeWrapper * opaque using {@link Activity#convertFromTranslucent()} and before it has been drawn * translucent again following a call to {@link * Activity#convertToTranslucent(TranslucentConversionListener)}. + * + * @hide */ public interface TranslucentConversionListener { /** diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index 361e7de..ec23f08 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -874,6 +874,20 @@ public abstract class CameraMetadata { public static final int STATISTICS_FACE_DETECT_MODE_FULL = 2; // + // Enumeration values for CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE + // + + /** + * @see CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE + */ + public static final int STATISTICS_LENS_SHADING_MAP_MODE_OFF = 0; + + /** + * @see CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE + */ + public static final int STATISTICS_LENS_SHADING_MAP_MODE_ON = 1; + + // // Enumeration values for CaptureRequest#TONEMAP_MODE // diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java index c3a636d..4608ab9 100644 --- a/core/java/android/hardware/camera2/CaptureRequest.java +++ b/core/java/android/hardware/camera2/CaptureRequest.java @@ -957,6 +957,22 @@ public final class CaptureRequest extends CameraMetadata implements Parcelable { /** * <p> + * Whether the HAL needs to output the lens + * shading map in output result metadata + * </p> + * <p> + * When set to ON, + * android.statistics.lensShadingMap must be provided in + * the output result metdata. + * </p> + * @see #STATISTICS_LENS_SHADING_MAP_MODE_OFF + * @see #STATISTICS_LENS_SHADING_MAP_MODE_ON + */ + public static final Key<Integer> STATISTICS_LENS_SHADING_MAP_MODE = + new Key<Integer>("android.statistics.lensShadingMapMode", int.class); + + /** + * <p> * Table mapping blue input values to output * values * </p> diff --git a/core/java/android/print/PrintAttributes.java b/core/java/android/print/PrintAttributes.java index b1e427e..a1ffa8c 100644 --- a/core/java/android/print/PrintAttributes.java +++ b/core/java/android/print/PrintAttributes.java @@ -226,6 +226,12 @@ public final class PrintAttributes implements Parcelable { StringBuilder builder = new StringBuilder(); builder.append("PrintAttributes{"); builder.append("mediaSize: ").append(mMediaSize); + if (mMediaSize != null) { + builder.append(", orientation: ").append(mMediaSize.isPortrait() + ? "portrait" : "landscape"); + } else { + builder.append(", orientation: ").append("null"); + } builder.append(", resolution: ").append(mResolution); builder.append(", margins: ").append(mMargins); builder.append(", colorMode: ").append(colorModeToString(mColorMode)); @@ -880,12 +886,6 @@ public final class PrintAttributes implements Parcelable { * @param bottomMils The bottom margin in mils (thousands of an inch). */ public Margins(int leftMils, int topMils, int rightMils, int bottomMils) { - if (leftMils > rightMils) { - throw new IllegalArgumentException("leftMils cannot be less than rightMils."); - } - if (topMils > bottomMils) { - throw new IllegalArgumentException("topMils cannot be less than bottomMils."); - } mTopMils = topMils; mLeftMils = leftMils; mRightMils = rightMils; diff --git a/core/java/android/print/PrintDocumentAdapter.java b/core/java/android/print/PrintDocumentAdapter.java index c81ca95..8ac34c1 100644 --- a/core/java/android/print/PrintDocumentAdapter.java +++ b/core/java/android/print/PrintDocumentAdapter.java @@ -20,8 +20,6 @@ import android.os.Bundle; import android.os.CancellationSignal; import android.os.ParcelFileDescriptor; -import java.util.List; - /** * Base class that provides the content of a document to be printed. * diff --git a/core/java/android/print/PrintJob.java b/core/java/android/print/PrintJob.java index de28bd3..42bea6d 100644 --- a/core/java/android/print/PrintJob.java +++ b/core/java/android/print/PrintJob.java @@ -16,7 +16,6 @@ package android.print; - /** * This class represents a print job from the perspective of * an application. diff --git a/core/java/android/printservice/PrintService.java b/core/java/android/printservice/PrintService.java index 0ffc40a..012e76a 100644 --- a/core/java/android/printservice/PrintService.java +++ b/core/java/android/printservice/PrintService.java @@ -20,7 +20,6 @@ import android.app.Service; import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.os.Build; import android.os.Handler; import android.os.IBinder; import android.os.Looper; diff --git a/core/java/android/text/AndroidBidi.java b/core/java/android/text/AndroidBidi.java index eacd40d..b1c07f5 100644 --- a/core/java/android/text/AndroidBidi.java +++ b/core/java/android/text/AndroidBidi.java @@ -60,6 +60,9 @@ import android.text.Layout.Directions; */ public static Directions directions(int dir, byte[] levels, int lstart, char[] chars, int cstart, int len) { + if (len == 0) { + return Layout.DIRS_ALL_LEFT_TO_RIGHT; + } int baseLevel = dir == Layout.DIR_LEFT_TO_RIGHT ? 0 : 1; int curLevel = levels[lstart]; diff --git a/core/java/android/transition/TextChange.java b/core/java/android/transition/TextChange.java index 4f14d46..0b1e4e1 100644 --- a/core/java/android/transition/TextChange.java +++ b/core/java/android/transition/TextChange.java @@ -137,14 +137,17 @@ public class TextChange extends Transition { @Override public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues) { - if (startValues == null || endValues == null || !(endValues.view instanceof TextView)) { + if (startValues == null || endValues == null || + !(startValues.view instanceof TextView) || !(endValues.view instanceof TextView)) { return null; } final TextView view = (TextView) endValues.view; Map<String, Object> startVals = startValues.values; Map<String, Object> endVals = endValues.values; - final CharSequence startText = (CharSequence) startVals.get(PROPNAME_TEXT); - final CharSequence endText = (CharSequence) endVals.get(PROPNAME_TEXT); + final CharSequence startText = startVals.get(PROPNAME_TEXT) != null ? + (CharSequence) startVals.get(PROPNAME_TEXT) : ""; + final CharSequence endText = endVals.get(PROPNAME_TEXT) != null ? + (CharSequence) endVals.get(PROPNAME_TEXT) : ""; if (!startText.equals(endText)) { view.setText(startText); Animator anim; diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 30531ed..8f8f9c6 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -8008,7 +8008,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, public boolean onKeyDown(int keyCode, KeyEvent event) { boolean result = false; - if (KeyEvent.isConfirmKey(event.getKeyCode())) { + if (KeyEvent.isConfirmKey(keyCode)) { if ((mViewFlags & ENABLED_MASK) == DISABLED) { return true; } @@ -8050,28 +8050,21 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @param event The KeyEvent object that defines the button action. */ public boolean onKeyUp(int keyCode, KeyEvent event) { - boolean result = false; - - switch (keyCode) { - case KeyEvent.KEYCODE_DPAD_CENTER: - case KeyEvent.KEYCODE_ENTER: { - if ((mViewFlags & ENABLED_MASK) == DISABLED) { - return true; - } - if ((mViewFlags & CLICKABLE) == CLICKABLE && isPressed()) { - setPressed(false); - - if (!mHasPerformedLongPress) { - // This is a tap, so remove the longpress check - removeLongPressCallback(); + if (KeyEvent.isConfirmKey(keyCode)) { + if ((mViewFlags & ENABLED_MASK) == DISABLED) { + return true; + } + if ((mViewFlags & CLICKABLE) == CLICKABLE && isPressed()) { + setPressed(false); - result = performClick(); - } + if (!mHasPerformedLongPress) { + // This is a tap, so remove the longpress check + removeLongPressCallback(); + return performClick(); } - break; } } - return result; + return false; } /** diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 8fc3ce3..15331dc 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -31,9 +31,8 @@ import android.os.Bundle; import android.os.CancellationSignal; import android.os.Looper; import android.os.Message; -import android.os.ParcelFileDescriptor; import android.os.StrictMode; -import android.print.PrintAttributes; +import android.print.PrintDocumentAdapter; import android.util.AttributeSet; import android.util.Log; import android.view.KeyEvent; @@ -1069,41 +1068,21 @@ public class WebView extends AbsoluteLayout } /** - * Exports the contents of this Webview as PDF. Only supported for API levels + * Creates a PrintDocumentAdapter that provides the content of this Webview for printing. + * Only supported for API levels * {@link android.os.Build.VERSION_CODES#KITKAT} and above. * - * TODO(sgurun) the parameter list is stale. Fix it before unhiding. - * - * @param fd The FileDescriptor to export the PDF contents to. Cannot be null. - * @param width The page width. Should be larger than 0. - * @param height The page height. Should be larger than 0. - * @param resultCallback A callback to be invoked when the PDF content is exported. - * A true indicates success, and a false failure. Cannot be null. - * @param cancellationSignal Signal for cancelling the PDF conversion request. Must not - * be null. - * - * The PDF conversion is done asynchronously and the PDF output is written to the provided - * file descriptor. The caller should not close the file descriptor until the resultCallback - * is called, indicating PDF conversion is complete. Webview will never close the file - * descriptor. - * Limitations: Webview cannot be drawn during the PDF export so the application is - * recommended to take it offscreen, or putting in a layer with an overlaid progress - * UI / spinner. - * - * If the caller cancels the task using the cancellationSignal, the cancellation will be - * acked using the resultCallback signal. - * - * Throws an exception if an IO error occurs accessing the file descriptor. - * - * TODO(sgurun) margins, explain the units, make it public. - * @hide + * The adapter works by converting the Webview contents to a PDF stream. The Webview cannot + * be drawn during the conversion process - any such draws are undefined. It is recommended + * to use a dedicated off screen Webview for the printing. If necessary, an application may + * temporarily hide a visible WebView by using a custom PrintDocumentAdapter instance + * wrapped around the object returned and observing the onStart and onFinish methods. See + * {@link android.print.PrintDocumentAdapter} for more information. */ - public void exportToPdf(ParcelFileDescriptor fd, PrintAttributes attributes, - ValueCallback<Boolean> resultCallback, CancellationSignal cancellationSignal) - throws java.io.IOException { + public PrintDocumentAdapter createPrintDocumentAdapter() { checkThread(); - if (DebugFlags.TRACE_API) Log.d(LOGTAG, "exportToPdf"); - mProvider.exportToPdf(fd, attributes, resultCallback, cancellationSignal); + if (DebugFlags.TRACE_API) Log.d(LOGTAG, "createPrintDocumentAdapter"); + return mProvider.createPrintDocumentAdapter(); } /** diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java index 3f22d53..e82ce30 100644 --- a/core/java/android/webkit/WebViewClassic.java +++ b/core/java/android/webkit/WebViewClassic.java @@ -62,7 +62,7 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.SystemClock; -import android.print.PrintAttributes; +import android.print.PrintDocumentAdapter; import android.security.KeyChain; import android.text.Editable; import android.text.InputType; @@ -2894,12 +2894,10 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc } /** - * See {@link WebView#exportToPdf()} + * See {@link WebView#createPrintDocumentAdapter()} */ @Override - public void exportToPdf(android.os.ParcelFileDescriptor fd, PrintAttributes attributes, - ValueCallback<Boolean> resultCallback, CancellationSignal cancellationSignal) - throws java.io.IOException { + public PrintDocumentAdapter createPrintDocumentAdapter() { // K-only API not implemented in WebViewClassic. throw new IllegalStateException("This API not supported on Android 4.3 and earlier"); } diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java index d625d8a..696aad4 100644 --- a/core/java/android/webkit/WebViewProvider.java +++ b/core/java/android/webkit/WebViewProvider.java @@ -25,10 +25,8 @@ import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.net.http.SslCertificate; import android.os.Bundle; -import android.os.CancellationSignal; import android.os.Message; -import android.os.ParcelFileDescriptor; -import android.print.PrintAttributes; +import android.print.PrintDocumentAdapter; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; @@ -149,9 +147,7 @@ public interface WebViewProvider { public Picture capturePicture(); - public void exportToPdf(ParcelFileDescriptor fd, PrintAttributes attributes, - ValueCallback<Boolean> resultCallback, CancellationSignal cancellationSignal) - throws java.io.IOException; + public PrintDocumentAdapter createPrintDocumentAdapter(); public float getScale(); diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index ab95d40..d4b834a 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -450,6 +450,13 @@ The default is false. --> <bool name="config_lidControlsSleep">false</bool> + <!-- Indicate whether to allow the device to suspend when the screen is off + due to the proximity sensor. This resource should only be set to true + if the sensor HAL correctly handles the proximity sensor as a wake-up source. + Otherwise, the device may fail to wake out of suspend reliably. + The default is false. --> + <bool name="config_suspendWhenScreenOffDueToProximity">false</bool> + <!-- Control the behavior when the user long presses the power button. 0 - Nothing 1 - Global actions menu @@ -793,6 +800,11 @@ <!-- 2 means give warning --> <integer name="config_datause_notification_type">2</integer> + <!-- If Voice Radio Technology is RIL_RADIO_TECHNOLOGY_LTE:14 this is the value + that should be used instead. A value of RIL_RADIO_TECHNOLOGY_UNKNOWN:0 means + there is no replacement value and VoLTE is assumed to be supported --> + <integer name="config_volte_replacement_rat">0</integer> + <!-- Flag indicating whether the current device is "voice capable". If true, this means that the device supports circuit-switched (i.e. voice) phone calls over the telephony network, and is diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 39e7127..44891a8 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -260,6 +260,7 @@ <java-symbol type="bool" name="config_sip_wifi_only" /> <java-symbol type="bool" name="config_sms_capable" /> <java-symbol type="bool" name="config_sms_utf8_support" /> + <java-symbol type="bool" name="config_suspendWhenScreenOffDueToProximity" /> <java-symbol type="bool" name="config_swipeDisambiguation" /> <java-symbol type="bool" name="config_syncstorageengine_masterSyncAutomatically" /> <java-symbol type="bool" name="config_telephony_use_own_number_for_voicemail" /> @@ -313,6 +314,7 @@ <java-symbol type="integer" name="config_multiuserMaximumUsers" /> <java-symbol type="integer" name="config_safe_media_volume_index" /> <java-symbol type="integer" name="config_mobile_mtu" /> + <java-symbol type="integer" name="config_volte_replacement_rat"/> <java-symbol type="color" name="tab_indicator_text_v4" /> diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp index 7ce15c5..fd9257a 100644 --- a/libs/hwui/DeferredDisplayList.cpp +++ b/libs/hwui/DeferredDisplayList.cpp @@ -66,23 +66,23 @@ public: virtual ~DrawBatch() { mOps.clear(); } - virtual void add(DrawOp* op, bool opaqueOverBounds) { + virtual void add(DrawOp* op, const DeferredDisplayState* state, bool opaqueOverBounds) { // NOTE: ignore empty bounds special case, since we don't merge across those ops - mBounds.unionWith(op->state.mBounds); + mBounds.unionWith(state->mBounds); mAllOpsOpaque &= opaqueOverBounds; - mOps.add(op); + mOps.add(OpStatePair(op, state)); } - bool intersects(Rect& rect) { + bool intersects(const Rect& rect) { if (!rect.intersects(mBounds)) return false; for (unsigned int i = 0; i < mOps.size(); i++) { - if (rect.intersects(mOps[i]->state.mBounds)) { + if (rect.intersects(mOps[i].state->mBounds)) { #if DEBUG_DEFER - DEFER_LOGD("op intersects with op %p with bounds %f %f %f %f:", mOps[i], - mOps[i]->state.mBounds.left, mOps[i]->state.mBounds.top, - mOps[i]->state.mBounds.right, mOps[i]->state.mBounds.bottom); - mOps[i]->output(2); + DEFER_LOGD("op intersects with op %p with bounds %f %f %f %f:", mOps[i].op, + mOps[i].state->mBounds.left, mOps[i].state->mBounds.top, + mOps[i].state->mBounds.right, mOps[i].state->mBounds.bottom); + mOps[i].op->output(2); #endif return true; } @@ -97,9 +97,9 @@ public: status_t status = DrawGlInfo::kStatusDone; DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance(); for (unsigned int i = 0; i < mOps.size(); i++) { - DrawOp* op = mOps[i]; - - renderer.restoreDisplayState(op->state); + DrawOp* op = mOps[i].op; + const DeferredDisplayState* state = mOps[i].state; + renderer.restoreDisplayState(*state); #if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS renderer.eventMark(op->name()); @@ -108,7 +108,7 @@ public: status |= op->applyDraw(renderer, dirty); #if DEBUG_MERGE_BEHAVIOR - Rect& bounds = mOps[i]->state.mBounds; + const Rect& bounds = state->mBounds; int batchColor = 0x1f000000; if (getBatchId() & 0x1) batchColor |= 0x0000ff; if (getBatchId() & 0x2) batchColor |= 0x00ff00; @@ -127,7 +127,7 @@ public: Region uncovered(android::Rect(bounds.left, bounds.top, bounds.right, bounds.bottom)); for (unsigned int i = 0; i < mOps.size(); i++) { - Rect &r = mOps[i]->state.mBounds; + const Rect &r = mOps[i].state->mBounds; uncovered.subtractSelf(android::Rect(r.left, r.top, r.right, r.bottom)); } return uncovered.isEmpty(); @@ -138,7 +138,7 @@ public: inline int count() const { return mOps.size(); } protected: - Vector<DrawOp*> mOps; + Vector<OpStatePair> mOps; Rect mBounds; // union of bounds of contained ops private: bool mAllOpsOpaque; @@ -184,19 +184,19 @@ public: * False positives can lead to information from the paints of subsequent merged operations being * dropped, so we make simplifying qualifications on the ops that can merge, per op type. */ - bool canMergeWith(DrawOp* op) { + bool canMergeWith(const DrawOp* op, const DeferredDisplayState* state) { bool isTextBatch = getBatchId() == DeferredDisplayList::kOpBatch_Text || getBatchId() == DeferredDisplayList::kOpBatch_ColorText; // Overlapping other operations is only allowed for text without shadow. For other ops, // multiDraw isn't guaranteed to overdraw correctly - if (!isTextBatch || op->state.mDrawModifiers.mHasShadow) { - if (intersects(op->state.mBounds)) return false; + if (!isTextBatch || state->mDrawModifiers.mHasShadow) { + if (intersects(state->mBounds)) return false; } - const DeferredDisplayState& lhs = op->state; - const DeferredDisplayState& rhs = mOps[0]->state; + const DeferredDisplayState* lhs = state; + const DeferredDisplayState* rhs = mOps[0].state; - if (NEQ_FALPHA(lhs.mAlpha, rhs.mAlpha)) return false; + if (NEQ_FALPHA(lhs->mAlpha, rhs->mAlpha)) return false; /* Clipping compatibility check * @@ -204,9 +204,9 @@ public: * clip for that side. */ const int currentFlags = mClipSideFlags; - const int newFlags = op->state.mClipSideFlags; + const int newFlags = state->mClipSideFlags; if (currentFlags != kClipSide_None || newFlags != kClipSide_None) { - const Rect& opBounds = op->state.mBounds; + const Rect& opBounds = state->mBounds; float boundsDelta = mBounds.left - opBounds.left; if (!checkSide(currentFlags, newFlags, kClipSide_Left, boundsDelta)) return false; boundsDelta = mBounds.top - opBounds.top; @@ -220,9 +220,9 @@ public: } // if paints are equal, then modifiers + paint attribs don't need to be compared - if (op->mPaint == mOps[0]->mPaint) return true; + if (op->mPaint == mOps[0].op->mPaint) return true; - if (op->getPaintAlpha() != mOps[0]->getPaintAlpha()) return false; + if (op->getPaintAlpha() != mOps[0].op->getPaintAlpha()) return false; /* Draw Modifiers compatibility check * @@ -236,8 +236,8 @@ public: * * These ignore cases prevent us from simply memcmp'ing the drawModifiers */ - const DrawModifiers& lhsMod = lhs.mDrawModifiers; - const DrawModifiers& rhsMod = rhs.mDrawModifiers; + const DrawModifiers& lhsMod = lhs->mDrawModifiers; + const DrawModifiers& rhsMod = rhs->mDrawModifiers; if (lhsMod.mShader != rhsMod.mShader) return false; if (lhsMod.mColorFilter != rhsMod.mColorFilter) return false; @@ -249,15 +249,15 @@ public: return true; } - virtual void add(DrawOp* op, bool opaqueOverBounds) { - DrawBatch::add(op, opaqueOverBounds); + virtual void add(DrawOp* op, DeferredDisplayState* state, bool opaqueOverBounds) { + DrawBatch::add(op, state, opaqueOverBounds); - const int newClipSideFlags = op->state.mClipSideFlags; + const int newClipSideFlags = state->mClipSideFlags; mClipSideFlags |= newClipSideFlags; - if (newClipSideFlags & kClipSide_Left) mClipRect.left = op->state.mClip.left; - if (newClipSideFlags & kClipSide_Top) mClipRect.top = op->state.mClip.top; - if (newClipSideFlags & kClipSide_Right) mClipRect.right = op->state.mClip.right; - if (newClipSideFlags & kClipSide_Bottom) mClipRect.bottom = op->state.mClip.bottom; + if (newClipSideFlags & kClipSide_Left) mClipRect.left = state->mClip.left; + if (newClipSideFlags & kClipSide_Top) mClipRect.top = state->mClip.top; + if (newClipSideFlags & kClipSide_Right) mClipRect.right = state->mClip.right; + if (newClipSideFlags & kClipSide_Bottom) mClipRect.bottom = state->mClip.bottom; } virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int index) { @@ -271,7 +271,7 @@ public: // clipping in the merged case is done ahead of time since all ops share the clip (if any) renderer.setupMergedMultiDraw(mClipSideFlags ? &mClipRect : NULL); - DrawOp* op = mOps[0]; + DrawOp* op = mOps[0].op; DisplayListLogBuffer& buffer = DisplayListLogBuffer::getInstance(); buffer.writeCommand(0, "multiDraw"); buffer.writeCommand(1, op->name()); @@ -297,11 +297,11 @@ private: class StateOpBatch : public Batch { public: // creates a single operation batch - StateOpBatch(StateOp* op) : mOp(op) {} + StateOpBatch(const StateOp* op, const DeferredDisplayState* state) : mOp(op), mState(state) {} virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int index) { DEFER_LOGD("replaying state op batch %p", this); - renderer.restoreDisplayState(mOp->state); + renderer.restoreDisplayState(*mState); // use invalid save count because it won't be used at flush time - RestoreToCountOp is the // only one to use it, and we don't use that class at flush time, instead calling @@ -313,16 +313,18 @@ public: private: const StateOp* mOp; + const DeferredDisplayState* mState; }; class RestoreToCountBatch : public Batch { public: - RestoreToCountBatch(StateOp* op, int restoreCount) : mOp(op), mRestoreCount(restoreCount) {} + RestoreToCountBatch(const StateOp* op, const DeferredDisplayState* state, int restoreCount) : + mOp(op), mState(state), mRestoreCount(restoreCount) {} virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int index) { DEFER_LOGD("batch %p restoring to count %d", this, mRestoreCount); - renderer.restoreDisplayState(mOp->state); + renderer.restoreDisplayState(*mState); renderer.restoreToCount(mRestoreCount); return DrawGlInfo::kStatusDone; } @@ -330,6 +332,8 @@ public: private: // we use the state storage for the RestoreToCountOp, but don't replay the op itself const StateOp* mOp; + const DeferredDisplayState* mState; + /* * The count used here represents the flush() time saveCount. This is as opposed to the * DisplayList record time, or defer() time values (which are RestoreToCountOp's mCount, and @@ -480,12 +484,27 @@ void DeferredDisplayList::addRestoreToCount(OpenGLRenderer& renderer, StateOp* o } void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) { - if (renderer.storeDisplayState(op->state, getDrawOpDeferFlags())) { + /* 1: op calculates local bounds */ + DeferredDisplayState* const state = createState(); + if (op->getLocalBounds(renderer.getDrawModifiers(), state->mBounds)) { + if (state->mBounds.isEmpty()) { + // valid empty bounds, don't bother deferring + tryRecycleState(state); + return; + } + } else { + state->mBounds.setEmpty(); + } + + /* 2: renderer calculates global bounds + stores state */ + if (renderer.storeDisplayState(*state, getDrawOpDeferFlags())) { + tryRecycleState(state); return; // quick rejected } + /* 3: ask op for defer info, given renderer state */ DeferInfo deferInfo; - op->onDefer(renderer, deferInfo); + op->onDefer(renderer, deferInfo, *state); // complex clip has a complex set of expectations on the renderer state - for now, avoid taking // the merge path in those cases @@ -493,8 +512,8 @@ void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) { deferInfo.opaqueOverBounds &= !recordingComplexClip() && mSaveStack.isEmpty(); if (CC_LIKELY(mAvoidOverdraw) && mBatches.size() && - op->state.mClipSideFlags != kClipSide_ConservativeFull && - deferInfo.opaqueOverBounds && op->state.mBounds.contains(mBounds)) { + state->mClipSideFlags != kClipSide_ConservativeFull && + deferInfo.opaqueOverBounds && state->mBounds.contains(mBounds)) { // avoid overdraw by resetting drawing state + discarding drawing ops discardDrawingBatches(mBatches.size() - 1); resetBatchingState(); @@ -503,7 +522,7 @@ void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) { if (CC_UNLIKELY(renderer.getCaches().drawReorderDisabled)) { // TODO: elegant way to reuse batches? DrawBatch* b = new DrawBatch(deferInfo); - b->add(op, deferInfo.opaqueOverBounds); + b->add(op, state, deferInfo.opaqueOverBounds); mBatches.add(b); return; } @@ -515,10 +534,10 @@ void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) { // (eventually, should be similar shader) int insertBatchIndex = mBatches.size(); if (!mBatches.isEmpty()) { - if (op->state.mBounds.isEmpty()) { + if (state->mBounds.isEmpty()) { // don't know the bounds for op, so add to last batch and start from scratch on next op DrawBatch* b = new DrawBatch(deferInfo); - b->add(op, deferInfo.opaqueOverBounds); + b->add(op, state, deferInfo.opaqueOverBounds); mBatches.add(b); resetBatchingState(); #if DEBUG_DEFER @@ -531,7 +550,7 @@ void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) { if (deferInfo.mergeable) { // Try to merge with any existing batch with same mergeId. if (mMergingBatches[deferInfo.batchId].get(deferInfo.mergeId, targetBatch)) { - if (!((MergingDrawBatch*) targetBatch)->canMergeWith(op)) { + if (!((MergingDrawBatch*) targetBatch)->canMergeWith(op, state)) { targetBatch = NULL; } } @@ -554,14 +573,14 @@ void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) { if (!targetBatch) break; // found insert position, quit } - if (overBatch->intersects(op->state.mBounds)) { + if (overBatch->intersects(state->mBounds)) { // NOTE: it may be possible to optimize for special cases where two operations // of the same batch/paint could swap order, such as with a non-mergeable // (clipped) and a mergeable text operation targetBatch = NULL; #if DEBUG_DEFER - DEFER_LOGD("op couldn't join batch %d, was intersected by batch %d", - targetIndex, i); + DEFER_LOGD("op couldn't join batch %p, was intersected by batch %d", + targetBatch, i); op->output(2); #endif break; @@ -586,14 +605,15 @@ void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) { mBatches.insertAt(targetBatch, insertBatchIndex); } - targetBatch->add(op, deferInfo.opaqueOverBounds); + targetBatch->add(op, state, deferInfo.opaqueOverBounds); } void DeferredDisplayList::storeStateOpBarrier(OpenGLRenderer& renderer, StateOp* op) { DEFER_LOGD("%p adding state op barrier at pos %d", this, mBatches.size()); - renderer.storeDisplayState(op->state, getStateOpDeferFlags()); - mBatches.add(new StateOpBatch(op)); + DeferredDisplayState* state = createState(); + renderer.storeDisplayState(*state, getStateOpDeferFlags()); + mBatches.add(new StateOpBatch(op, state)); resetBatchingState(); } @@ -604,8 +624,9 @@ void DeferredDisplayList::storeRestoreToCountBarrier(OpenGLRenderer& renderer, S // store displayState for the restore operation, as it may be associated with a saveLayer that // doesn't have kClip_SaveFlag set - renderer.storeDisplayState(op->state, getStateOpDeferFlags()); - mBatches.add(new RestoreToCountBatch(op, newSaveCount)); + DeferredDisplayState* state = createState(); + renderer.storeDisplayState(*state, getStateOpDeferFlags()); + mBatches.add(new RestoreToCountBatch(op, state, newSaveCount)); resetBatchingState(); } diff --git a/libs/hwui/DeferredDisplayList.h b/libs/hwui/DeferredDisplayList.h index 1ef0152..3dcbd0b 100644 --- a/libs/hwui/DeferredDisplayList.h +++ b/libs/hwui/DeferredDisplayList.h @@ -18,11 +18,13 @@ #define ANDROID_HWUI_DEFERRED_DISPLAY_LIST_H #include <utils/Errors.h> +#include <utils/LinearAllocator.h> #include <utils/Vector.h> +#include <utils/TinyHashMap.h> #include "Matrix.h" +#include "OpenGLRenderer.h" #include "Rect.h" -#include "utils/TinyHashMap.h" class SkBitmap; @@ -34,6 +36,8 @@ class DrawOp; class SaveOp; class SaveLayerOp; class StateOp; + +class DeferredDisplayState; class OpenGLRenderer; class Batch; @@ -42,6 +46,38 @@ class MergingDrawBatch; typedef const void* mergeid_t; +class DeferredDisplayState { +public: + /** static void* operator new(size_t size); PURPOSELY OMITTED **/ + static void* operator new(size_t size, LinearAllocator& allocator) { + return allocator.alloc(size); + } + + // global op bounds, mapped by mMatrix to be in screen space coordinates, clipped + Rect mBounds; + + // the below are set and used by the OpenGLRenderer at record and deferred playback + bool mClipValid; + Rect mClip; + int mClipSideFlags; // specifies which sides of the bounds are clipped, unclipped if cleared + bool mClipped; + mat4 mMatrix; + DrawModifiers mDrawModifiers; + float mAlpha; +}; + +class OpStatePair { +public: + OpStatePair() + : op(NULL), state(NULL) {} + OpStatePair(DrawOp* newOp, const DeferredDisplayState* newState) + : op(newOp), state(newState) {} + OpStatePair(const OpStatePair& other) + : op(other.op), state(other.state) {} + DrawOp* op; + const DeferredDisplayState* state; +}; + class DeferredDisplayList { public: DeferredDisplayList(const Rect& bounds, bool avoidOverdraw = true) : @@ -84,6 +120,14 @@ public: void addDrawOp(OpenGLRenderer& renderer, DrawOp* op); private: + DeferredDisplayState* createState() { + return new (mAllocator) DeferredDisplayState(); + } + + void tryRecycleState(DeferredDisplayState* state) { + mAllocator.rewindIfLastAlloc(state, sizeof(DeferredDisplayState)); + } + /** * Resets the batching back-pointers, creating a barrier in the operation stream so that no ops * added in the future will be inserted into a batch that already exist. @@ -131,6 +175,8 @@ private: * collide, which avoids the need to resolve mergeid collisions. */ TinyHashMap<mergeid_t, DrawBatch*> mMergingBatches[kOpBatch_Count]; + + LinearAllocator mAllocator; }; /** diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h index 194be9e..1cd5f1c 100644 --- a/libs/hwui/DisplayList.h +++ b/libs/hwui/DisplayList.h @@ -26,6 +26,7 @@ #include <private/hwui/DrawGlInfo.h> +#include <utils/LinearAllocator.h> #include <utils/RefBase.h> #include <utils/SortedVector.h> #include <utils/String8.h> @@ -35,8 +36,6 @@ #include <androidfw/ResourceTypes.h> -#include "utils/LinearAllocator.h" - #include "Debug.h" #define TRANSLATION 0x0001 @@ -114,7 +113,6 @@ public: void initFromDisplayListRenderer(const DisplayListRenderer& recorder, bool reusing = false); - void defer(DeferStateStruct& deferStruct, const int level); void replay(ReplayStateStruct& replayStruct, const int level); diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h index 42e11d0..a17942e 100644 --- a/libs/hwui/DisplayListOp.h +++ b/libs/hwui/DisplayListOp.h @@ -82,15 +82,6 @@ public: // NOTE: it would be nice to declare constants and overriding the implementation in each op to // point at the constants, but that seems to require a .cpp file virtual const char* name() = 0; - - /** - * Stores the relevant canvas state of the object between deferral and replay (if the canvas - * state supports being stored) See OpenGLRenderer::simpleClipAndState() - * - * TODO: don't reserve space for StateOps that won't be deferred - */ - DeferredDisplayState state; - }; class StateOp : public DisplayListOp { @@ -129,14 +120,6 @@ public: return; } - if (getLocalBounds(state.mBounds)) { - // valid empty bounds, don't bother deferring - if (state.mBounds.isEmpty()) return; - } else { - // empty bounds signify bounds can't be calculated - state.mBounds.setEmpty(); - } - deferStruct.mDeferredList.addDrawOp(deferStruct.mRenderer, this); } @@ -159,11 +142,11 @@ public: * reducing which operations are tagged as mergeable. */ virtual status_t multiDraw(OpenGLRenderer& renderer, Rect& dirty, - const Vector<DrawOp*>& ops, const Rect& bounds) { + const Vector<OpStatePair>& ops, const Rect& bounds) { status_t status = DrawGlInfo::kStatusDone; for (unsigned int i = 0; i < ops.size(); i++) { - renderer.restoreDisplayState(ops[i]->state, true); - status |= ops[i]->applyDraw(renderer, dirty); + renderer.restoreDisplayState(*(ops[i].state), true); + status |= ops[i].op->applyDraw(renderer, dirty); } return status; } @@ -178,20 +161,23 @@ public: * * if a subclass can set deferInfo.mergeable to true, it should implement multiDraw() */ - virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo) {} + virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, + const DeferredDisplayState& state) {} /** * Query the conservative, local bounds (unmapped) bounds of the op. * * returns true if bounds exist */ - virtual bool getLocalBounds(Rect& localBounds) { return false; } + virtual bool getLocalBounds(const DrawModifiers& drawModifiers, Rect& localBounds) { + return false; + } // TODO: better refine localbounds usage void setQuickRejected(bool quickRejected) { mQuickRejected = quickRejected; } bool getQuickRejected() { return mQuickRejected; } - inline int getPaintAlpha() { + inline int getPaintAlpha() const { return OpenGLRenderer::getAlphaDirect(mPaint); } @@ -208,7 +194,7 @@ protected: // Helper method for determining op opaqueness. Assumes op fills its bounds in local // coordinates, and that paint's alpha is used - inline bool isOpaqueOverBounds() { + inline bool isOpaqueOverBounds(const DeferredDisplayState& state) { // ensure that local bounds cover mapped bounds if (!state.mMatrix.isSimple()) return false; @@ -251,12 +237,13 @@ public: // default empty constructor for bounds, to be overridden in child constructor body DrawBoundedOp(SkPaint* paint): DrawOp(paint) { } - bool getLocalBounds(Rect& localBounds) { + bool getLocalBounds(const DrawModifiers& drawModifiers, Rect& localBounds) { localBounds.set(mLocalBounds); - if (state.mDrawModifiers.mHasShadow) { + if (drawModifiers.mHasShadow) { + // TODO: inspect paint's looper directly Rect shadow(mLocalBounds); - shadow.translate(state.mDrawModifiers.mShadowDx, state.mDrawModifiers.mShadowDy); - shadow.outset(state.mDrawModifiers.mShadowRadius); + shadow.translate(drawModifiers.mShadowDx, drawModifiers.mShadowDy); + shadow.outset(drawModifiers.mShadowRadius); localBounds.unionWith(shadow); } return true; @@ -777,8 +764,10 @@ public: * the current layer, if any. */ virtual status_t multiDraw(OpenGLRenderer& renderer, Rect& dirty, - const Vector<DrawOp*>& ops, const Rect& bounds) { - renderer.restoreDisplayState(state, true); // restore all but the clip + const Vector<OpStatePair>& ops, const Rect& bounds) { + const DeferredDisplayState& firstState = *(ops[0].state); + renderer.restoreDisplayState(firstState, true); // restore all but the clip + TextureVertex vertices[6 * ops.size()]; TextureVertex* vertex = &vertices[0]; @@ -788,14 +777,15 @@ public: // TODO: manually handle rect clip for bitmaps by adjusting texCoords per op, // and allowing them to be merged in getBatchId() for (unsigned int i = 0; i < ops.size(); i++) { - const Rect& opBounds = ops[i]->state.mBounds; + const DeferredDisplayState& state = *(ops[i].state); + const Rect& opBounds = state.mBounds; // When we reach multiDraw(), the matrix can be either // pureTranslate or simple (translate and/or scale). // If the matrix is not pureTranslate, then we have a scale - if (!ops[i]->state.mMatrix.isPureTranslate()) transformed = true; + if (state.mMatrix.isPureTranslate()) transformed = true; Rect texCoords(0, 0, 1, 1); - ((DrawBitmapOp*) ops[i])->mUvMapper.map(texCoords); + ((DrawBitmapOp*) ops[i].op)->mUvMapper.map(texCoords); SET_TEXTURE(vertex, opBounds, bounds, texCoords, left, top); SET_TEXTURE(vertex, opBounds, bounds, texCoords, right, top); @@ -806,8 +796,7 @@ public: SET_TEXTURE(vertex, opBounds, bounds, texCoords, right, bottom); if (hasLayer) { - const Rect& dirty = ops[i]->state.mBounds; - renderer.dirtyLayer(dirty.left, dirty.top, dirty.right, dirty.bottom); + renderer.dirtyLayer(opBounds.left, opBounds.top, opBounds.right, opBounds.bottom); } } @@ -821,7 +810,8 @@ public: virtual const char* name() { return "DrawBitmap"; } - virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo) { + virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, + const DeferredDisplayState& state) { deferInfo.batchId = DeferredDisplayList::kOpBatch_Bitmap; deferInfo.mergeId = getAtlasEntry() ? (mergeid_t) mEntry->getMergeId() : (mergeid_t) mBitmap; @@ -861,7 +851,8 @@ public: virtual const char* name() { return "DrawBitmapMatrix"; } - virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo) { + virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, + const DeferredDisplayState& state) { deferInfo.batchId = DeferredDisplayList::kOpBatch_Bitmap; } @@ -890,7 +881,8 @@ public: virtual const char* name() { return "DrawBitmapRect"; } - virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo) { + virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, + const DeferredDisplayState& state) { deferInfo.batchId = DeferredDisplayList::kOpBatch_Bitmap; } @@ -915,7 +907,8 @@ public: virtual const char* name() { return "DrawBitmapData"; } - virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo) { + virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, + const DeferredDisplayState& state) { deferInfo.batchId = DeferredDisplayList::kOpBatch_Bitmap; } }; @@ -939,7 +932,8 @@ public: virtual const char* name() { return "DrawBitmapMesh"; } - virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo) { + virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, + const DeferredDisplayState& state) { deferInfo.batchId = DeferredDisplayList::kOpBatch_Bitmap; } @@ -989,15 +983,16 @@ public: * is also responsible for dirtying the current layer, if any. */ virtual status_t multiDraw(OpenGLRenderer& renderer, Rect& dirty, - const Vector<DrawOp*>& ops, const Rect& bounds) { - renderer.restoreDisplayState(state, true); + const Vector<OpStatePair>& ops, const Rect& bounds) { + const DeferredDisplayState& firstState = *(ops[0].state); + renderer.restoreDisplayState(firstState, true); // restore all but the clip // Batches will usually contain a small number of items so it's // worth performing a first iteration to count the exact number // of vertices we need in the new mesh uint32_t totalVertices = 0; for (unsigned int i = 0; i < ops.size(); i++) { - totalVertices += ((DrawPatchOp*) ops[i])->getMesh(renderer)->verticesCount; + totalVertices += ((DrawPatchOp*) ops[i].op)->getMesh(renderer)->verticesCount; } const bool hasLayer = renderer.hasLayer(); @@ -1012,7 +1007,8 @@ public: // enforces ops drawn by this function to have a pure translate or // identity matrix for (unsigned int i = 0; i < ops.size(); i++) { - DrawPatchOp* patchOp = (DrawPatchOp*) ops[i]; + DrawPatchOp* patchOp = (DrawPatchOp*) ops[i].op; + const DeferredDisplayState* state = ops[i].state; const Patch* opMesh = patchOp->getMesh(renderer); uint32_t vertexCount = opMesh->verticesCount; if (vertexCount == 0) continue; @@ -1020,9 +1016,9 @@ public: // We use the bounds to know where to translate our vertices // Using patchOp->state.mBounds wouldn't work because these // bounds are clipped - const float tx = (int) floorf(patchOp->state.mMatrix.getTranslateX() + + const float tx = (int) floorf(state->mMatrix.getTranslateX() + patchOp->mLocalBounds.left + 0.5f); - const float ty = (int) floorf(patchOp->state.mMatrix.getTranslateY() + + const float ty = (int) floorf(state->mMatrix.getTranslateY() + patchOp->mLocalBounds.top + 0.5f); // Copy & transform all the vertices for the current operation @@ -1074,12 +1070,13 @@ public: virtual const char* name() { return "DrawPatch"; } - virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo) { + virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, + const DeferredDisplayState& state) { deferInfo.batchId = DeferredDisplayList::kOpBatch_Patch; deferInfo.mergeId = getAtlasEntry() ? (mergeid_t) mEntry->getMergeId() : (mergeid_t) mBitmap; deferInfo.mergeable = state.mMatrix.isPureTranslate() && OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode; - deferInfo.opaqueOverBounds = isOpaqueOverBounds() && mBitmap->isOpaque(); + deferInfo.opaqueOverBounds = isOpaqueOverBounds(state) && mBitmap->isOpaque(); } private: @@ -1119,7 +1116,7 @@ public: DrawStrokableOp(float left, float top, float right, float bottom, SkPaint* paint) : DrawBoundedOp(left, top, right, bottom, paint) {}; - bool getLocalBounds(Rect& localBounds) { + bool getLocalBounds(const DrawModifiers& drawModifiers, Rect& localBounds) { localBounds.set(mLocalBounds); if (mPaint && mPaint->getStyle() != SkPaint::kFill_Style) { localBounds.outset(strokeWidthOutset()); @@ -1127,7 +1124,8 @@ public: return true; } - virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo) { + virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, + const DeferredDisplayState& state) { if (mPaint->getPathEffect()) { deferInfo.batchId = DeferredDisplayList::kOpBatch_AlphaMaskTexture; } else { @@ -1152,9 +1150,10 @@ public: OP_LOG("Draw Rect "RECT_STRING, RECT_ARGS(mLocalBounds)); } - virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo) { - DrawStrokableOp::onDefer(renderer, deferInfo); - deferInfo.opaqueOverBounds = isOpaqueOverBounds() && + virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, + const DeferredDisplayState& state) { + DrawStrokableOp::onDefer(renderer, deferInfo, state); + deferInfo.opaqueOverBounds = isOpaqueOverBounds(state) && mPaint->getStyle() == SkPaint::kFill_Style; } @@ -1177,7 +1176,8 @@ public: virtual const char* name() { return "DrawRects"; } - virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo) { + virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, + const DeferredDisplayState& state) { deferInfo.batchId = DeferredDisplayList::kOpBatch_Vertices; } @@ -1289,7 +1289,8 @@ public: return renderer.drawPath(mPath, getPaint(renderer)); } - virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo) { + virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, + const DeferredDisplayState& state) { SkPaint* paint = getPaint(renderer); renderer.getCaches().pathCache.precache(mPath, paint); @@ -1324,7 +1325,8 @@ public: virtual const char* name() { return "DrawLines"; } - virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo) { + virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, + const DeferredDisplayState& state) { deferInfo.batchId = mPaint->isAntiAlias() ? DeferredDisplayList::kOpBatch_AlphaVertices : DeferredDisplayList::kOpBatch_Vertices; @@ -1360,7 +1362,8 @@ public: OP_LOG("Draw some text, %d bytes", mBytesCount); } - virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo) { + virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, + const DeferredDisplayState& state) { SkPaint* paint = getPaint(renderer); FontRenderer& fontRenderer = renderer.getCaches().fontRenderer->getFontRenderer(paint); fontRenderer.precache(paint, mText, mCount, mat4::identity()); @@ -1425,7 +1428,8 @@ public: memset(&mPrecacheTransform.data[0], 0xff, 16 * sizeof(float)); } - virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo) { + virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, + const DeferredDisplayState& state) { SkPaint* paint = getPaint(renderer); FontRenderer& fontRenderer = renderer.getCaches().fontRenderer->getFontRenderer(paint); const mat4& transform = renderer.findBestFontTransform(state.mMatrix); @@ -1448,19 +1452,20 @@ public: virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { Rect bounds; - getLocalBounds(bounds); + getLocalBounds(renderer.getDrawModifiers(), bounds); return renderer.drawText(mText, mBytesCount, mCount, mX, mY, mPositions, getPaint(renderer), mTotalAdvance, bounds); } virtual status_t multiDraw(OpenGLRenderer& renderer, Rect& dirty, - const Vector<DrawOp*>& ops, const Rect& bounds) { + const Vector<OpStatePair>& ops, const Rect& bounds) { status_t status = DrawGlInfo::kStatusDone; for (unsigned int i = 0; i < ops.size(); i++) { + const DeferredDisplayState& state = *(ops[i].state); DrawOpMode drawOpMode = (i == ops.size() - 1) ? kDrawOpMode_Flush : kDrawOpMode_Defer; - renderer.restoreDisplayState(ops[i]->state, true); // restore all but the clip + renderer.restoreDisplayState(state, true); // restore all but the clip - DrawTextOp& op = *((DrawTextOp*)ops[i]); + DrawTextOp& op = *((DrawTextOp*)ops[i].op); // quickReject() will not occure in drawText() so we can use mLocalBounds // directly, we do not need to account for shadow by calling getLocalBounds() status |= renderer.drawText(op.mText, op.mBytesCount, op.mCount, op.mX, op.mY, diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp index 90dcf93..8866029 100644 --- a/libs/hwui/DisplayListRenderer.cpp +++ b/libs/hwui/DisplayListRenderer.cpp @@ -505,17 +505,12 @@ void DisplayListRenderer::addStateOp(StateOp* op) { void DisplayListRenderer::addDrawOp(DrawOp* op) { Rect localBounds; - if (mDrawModifiers.mHasShadow) { - op->state.mDrawModifiers = mDrawModifiers; - } - if (op->getLocalBounds(localBounds)) { + if (op->getLocalBounds(mDrawModifiers, localBounds)) { bool rejected = quickRejectNoScissor(localBounds.left, localBounds.top, localBounds.right, localBounds.bottom); op->setQuickRejected(rejected); } - if (mDrawModifiers.mHasShadow) { - op->state.mDrawModifiers.reset(); - } + mHasDrawOps = true; addOpInternal(op); } diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index 54f6d76..f74df97 100644 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -98,24 +98,11 @@ enum ClipSideFlags { kClipSide_ConservativeFull = 0x1F }; -struct DeferredDisplayState { - // global op bounds, mapped by mMatrix to be in screen space coordinates, clipped - Rect mBounds; - - // the below are set and used by the OpenGLRenderer at record and deferred playback - bool mClipValid; - Rect mClip; - int mClipSideFlags; // specifies which sides of the bounds are clipped, unclipped if cleared - bool mClipped; - mat4 mMatrix; - DrawModifiers mDrawModifiers; - float mAlpha; -}; - /////////////////////////////////////////////////////////////////////////////// // Renderer /////////////////////////////////////////////////////////////////////////////// +class DeferredDisplayState; class DisplayList; class TextSetupFunctor; class VertexBuffer; @@ -423,7 +410,7 @@ public: return getXfermode(paint->getXfermode()); } - static inline int getAlphaDirect(SkPaint* paint) { + static inline int getAlphaDirect(const SkPaint* paint) { if (!paint) return 255; return paint->getAlpha(); } diff --git a/packages/PrintSpooler/src/com/android/printspooler/FusedPrintersProvider.java b/packages/PrintSpooler/src/com/android/printspooler/FusedPrintersProvider.java index 7d6ca56..e47bf0c 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/FusedPrintersProvider.java +++ b/packages/PrintSpooler/src/com/android/printspooler/FusedPrintersProvider.java @@ -57,7 +57,7 @@ import java.util.Map; public class FusedPrintersProvider extends Loader<List<PrinterInfo>> { private static final String LOG_TAG = "FusedPrintersProvider"; - private static final boolean DEBUG = true && Build.IS_DEBUGGABLE; + private static final boolean DEBUG = false; private static final double WEIGHT_DECAY_COEFFICIENT = 0.95f; diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java index 1040edf..514e8ca 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java +++ b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java @@ -42,6 +42,7 @@ import android.print.IPrintDocumentAdapter; import android.print.IWriteResultCallback; import android.print.PageRange; import android.print.PrintAttributes; +import android.print.PrintAttributes.Margins; import android.print.PrintAttributes.MediaSize; import android.print.PrintAttributes.Resolution; import android.print.PrintDocumentAdapter; @@ -341,7 +342,11 @@ public class PrintJobConfigActivity extends Activity { if (!mController.hasStarted()) { mController.start(); } - if (!printAttributesChanged() && mDocument.info != null) { + if (!printAttributesChanged()) { + if (mDocument.info == null) { + // We are waiting for the result of a layout, so do nothing. + return; + } // If the attributes didn't change and we have done a layout, then // we do not do a layout but may have to ask the app to write some // pages. Hence, pretend layout completed and nothing changed, so @@ -738,6 +743,8 @@ public class PrintJobConfigActivity extends Activity { private PrinterInfo mCurrentPrinter; + private boolean mRequestedCurrentPrinterRefresh; + private final OnItemSelectedListener mOnItemSelectedListener = new AdapterView.OnItemSelectedListener() { @Override @@ -757,34 +764,34 @@ public class PrintJobConfigActivity extends Activity { return; } - mCurrPrintAttributes.clear(); + mRequestedCurrentPrinterRefresh = false; - PrinterInfo printer = (PrinterInfo) mDestinationSpinnerAdapter + mCurrentPrinter = (PrinterInfo) mDestinationSpinnerAdapter .getItem(position); PrintSpoolerService.peekInstance().setPrintJobPrinterNoPersistence( - mPrintJobId, printer); + mPrintJobId, mCurrentPrinter); - if (printer != null) { - PrinterCapabilitiesInfo capabilities = printer.getCapabilities(); - if (capabilities == null) { - //TODO: We need a timeout for the update. - mEditor.refreshCurrentPrinter(); - } else { - capabilities.getDefaults(mCurrPrintAttributes); - if (!mController.hasStarted()) { - mController.start(); - } - mController.update(); - } + if (mCurrentPrinter.getStatus() == PrinterInfo.STATUS_UNAVAILABLE) { + updateUi(); + return; } - mCurrentPrinter = printer; - - updateUiForNewPrinterCapabilities(); + PrinterCapabilitiesInfo capabilities = mCurrentPrinter.getCapabilities(); + if (capabilities == null) { + // TODO: We need a timeout for the update. + mRequestedCurrentPrinterRefresh = true; + updateUi(); + refreshCurrentPrinter(); + } else { + updatePrintAttributes(capabilities); + updateUi(); + mController.update(); + } } else if (spinner == mMediaSizeSpinner) { - if (mIgnoreNextMediaSizeChange) { - mIgnoreNextMediaSizeChange = false; + if (mOldMediaSizeSelectionIndex + == mMediaSizeSpinner.getSelectedItemPosition()) { + mOldMediaSizeSelectionIndex = AdapterView.INVALID_POSITION; return; } SpinnerItem<MediaSize> mediaItem = mMediaSizeSpinnerAdapter.getItem(position); @@ -793,8 +800,9 @@ public class PrintJobConfigActivity extends Activity { mController.update(); } } else if (spinner == mColorModeSpinner) { - if (mIgnoreNextColorModeChange) { - mIgnoreNextColorModeChange = false; + if (mOldColorModeSelectionIndex + == mColorModeSpinner.getSelectedItemPosition()) { + mOldColorModeSelectionIndex = AdapterView.INVALID_POSITION; return; } SpinnerItem<Integer> colorModeItem = @@ -810,16 +818,7 @@ public class PrintJobConfigActivity extends Activity { } SpinnerItem<Integer> orientationItem = mOrientationSpinnerAdapter.getItem(position); - MediaSize mediaSize = mCurrPrintAttributes.getMediaSize(); - if (orientationItem.value == ORIENTATION_PORTRAIT) { - if (!mediaSize.isPortrait()) { - mCurrPrintAttributes.setMediaSize(mediaSize.asPortrait()); - } - } else { - if (mediaSize.isPortrait()) { - mCurrPrintAttributes.setMediaSize(mediaSize.asLandscape()); - } - } + setCurrentPrintAttributesOrientation(orientationItem.value); if (!hasErrors()) { mController.update(); } @@ -841,6 +840,105 @@ public class PrintJobConfigActivity extends Activity { } }; + private void setCurrentPrintAttributesOrientation(int orientation) { + MediaSize mediaSize = mCurrPrintAttributes.getMediaSize(); + if (orientation == ORIENTATION_PORTRAIT) { + if (!mediaSize.isPortrait()) { + // Rotate the media size. + mCurrPrintAttributes.setMediaSize(mediaSize.asPortrait()); + + // Rotate the resolution. + Resolution oldResolution = mCurrPrintAttributes.getResolution(); + Resolution newResolution = new Resolution( + oldResolution.getId(), + oldResolution.getLabel(getPackageManager()), + oldResolution.getVerticalDpi(), + oldResolution.getHorizontalDpi()); + mCurrPrintAttributes.setResolution(newResolution); + + // Rotate the physical margins. + Margins oldMargins = mCurrPrintAttributes.getMargins(); + Margins newMargins = new Margins( + oldMargins.getBottomMils(), + oldMargins.getLeftMils(), + oldMargins.getTopMils(), + oldMargins.getRightMils()); + mCurrPrintAttributes.setMargins(newMargins); + } + } else { + if (mediaSize.isPortrait()) { + // Rotate the media size. + mCurrPrintAttributes.setMediaSize(mediaSize.asLandscape()); + + // Rotate the resolution. + Resolution oldResolution = mCurrPrintAttributes.getResolution(); + Resolution newResolution = new Resolution( + oldResolution.getId(), + oldResolution.getLabel(getPackageManager()), + oldResolution.getVerticalDpi(), + oldResolution.getHorizontalDpi()); + mCurrPrintAttributes.setResolution(newResolution); + + // Rotate the physical margins. + Margins oldMargins = mCurrPrintAttributes.getMargins(); + Margins newMargins = new Margins( + oldMargins.getTopMils(), + oldMargins.getRightMils(), + oldMargins.getBottomMils(), + oldMargins.getLeftMils()); + mCurrPrintAttributes.setMargins(newMargins); + } + } + } + + private void updatePrintAttributes(PrinterCapabilitiesInfo capabilities) { + PrintAttributes defaults = mTempPrintAttributes; + capabilities.getDefaults(defaults); + + // Media size. + MediaSize currMediaSize = mCurrPrintAttributes.getMediaSize(); + if (currMediaSize == null) { + mCurrPrintAttributes.setMediaSize(defaults.getMediaSize()); + } else { + MediaSize currMediaSizePortrait = currMediaSize.asPortrait(); + List<MediaSize> mediaSizes = capabilities.getMediaSizes(); + final int mediaSizeCount = mediaSizes.size(); + for (int i = 0; i < mediaSizeCount; i++) { + MediaSize mediaSize = mediaSizes.get(i); + if (currMediaSizePortrait.equals(mediaSize.asPortrait())) { + mCurrPrintAttributes.setMediaSize(mediaSize); + break; + } + } + } + + // Color mode. + final int colorMode = mCurrPrintAttributes.getColorMode(); + if ((capabilities.getColorModes() & colorMode) == 0) { + mCurrPrintAttributes.setColorMode(colorMode); + } + + // Resolution + Resolution resolution = mCurrPrintAttributes.getResolution(); + if (resolution == null || !capabilities.getResolutions().contains(resolution)) { + mCurrPrintAttributes.setResolution(defaults.getResolution()); + } + + // Margins. + Margins margins = mCurrPrintAttributes.getMargins(); + if (margins == null) { + mCurrPrintAttributes.setMargins(defaults.getMargins()); + } else { + Margins minMargins = capabilities.getMinMargins(); + if (margins.getLeftMils() < minMargins.getLeftMils() + || margins.getTopMils() < minMargins.getTopMils() + || margins.getRightMils() > minMargins.getRightMils() + || margins.getBottomMils() > minMargins.getBottomMils()) { + mCurrPrintAttributes.setMargins(defaults.getMargins()); + } + } + } + private final TextWatcher mCopiesTextWatcher = new TextWatcher() { @Override public void onTextChanged(CharSequence s, int start, int before, int count) { @@ -952,8 +1050,8 @@ public class PrintJobConfigActivity extends Activity { private int mEditorState; private boolean mIgnoreNextDestinationChange; - private boolean mIgnoreNextMediaSizeChange; - private boolean mIgnoreNextColorModeChange; + private int mOldMediaSizeSelectionIndex; + private int mOldColorModeSelectionIndex; private boolean mIgnoreNextOrientationChange; private boolean mIgnoreNextRangeOptionChange; private boolean mIgnoreNextCopiesChange; @@ -993,11 +1091,22 @@ public class PrintJobConfigActivity extends Activity { // capabilities, we refresh it. if (mCurrentPrinter.getStatus() == PrinterInfo.STATUS_UNAVAILABLE && printer.getStatus() != PrinterInfo.STATUS_UNAVAILABLE - && printer.getCapabilities() == null) { + && printer.getCapabilities() == null + && !mRequestedCurrentPrinterRefresh) { + mRequestedCurrentPrinterRefresh = true; refreshCurrentPrinter(); return; } + // We just refreshed the current printer. + if (printer.getCapabilities() != null + && mRequestedCurrentPrinterRefresh) { + mRequestedCurrentPrinterRefresh = false; + updatePrintAttributes(printer.getCapabilities()); + updateUi(); + mController.update(); + } + // Update the UI if capabilities changed. boolean capabilitiesChanged = false; @@ -1010,14 +1119,18 @@ public class PrintJobConfigActivity extends Activity { capabilitiesChanged = true; } - if (capabilitiesChanged) { - // Update the current printer. + // Update the UI if the status changed. + final boolean statusChanged = mCurrentPrinter.getStatus() + != printer.getStatus(); + + // Update the printer with the latest info. + if (!mCurrentPrinter.equals(printer)) { mCurrentPrinter.copyFrom(printer); + } - // If something changed during UI update... + if (capabilitiesChanged || statusChanged) { + // If something changed during update... if (updateUi()) { - // Update current attributes. - printer.getCapabilities().getDefaults(mCurrPrintAttributes); // Update the document. mController.update(); } @@ -1031,7 +1144,7 @@ public class PrintJobConfigActivity extends Activity { @Override public void onInvalidated() { - updateUiForNewPrinterCapabilities(); + /* do nothing - we always have one fake PDF printer */ } }); @@ -1072,6 +1185,10 @@ public class PrintJobConfigActivity extends Activity { showUi(UI_EDITING_PRINT_JOB, null); bindUi(); + + mCurrentPrinter = mDestinationSpinnerAdapter.mFakePdfPrinter; + updatePrintAttributes(mCurrentPrinter.getCapabilities()); + updateUi(); } @@ -1393,7 +1510,7 @@ public class PrintJobConfigActivity extends Activity { mMediaSizeSpinner.setAdapter(mMediaSizeSpinnerAdapter); mMediaSizeSpinner.setOnItemSelectedListener(mOnItemSelectedListener); if (mMediaSizeSpinnerAdapter.getCount() > 0) { - mIgnoreNextMediaSizeChange = true; + mOldMediaSizeSelectionIndex = 0; } // Color mode. @@ -1401,7 +1518,7 @@ public class PrintJobConfigActivity extends Activity { mColorModeSpinner.setAdapter(mColorModeSpinnerAdapter); mColorModeSpinner.setOnItemSelectedListener(mOnItemSelectedListener); if (mColorModeSpinnerAdapter.getCount() > 0) { - mIgnoreNextColorModeChange = true; + mOldColorModeSelectionIndex = 0; } // Orientation @@ -1454,7 +1571,8 @@ public class PrintJobConfigActivity extends Activity { Object item = mDestinationSpinnerAdapter.getItem(selectedIndex); if (item instanceof PrinterInfo) { PrinterInfo printer = (PrinterInfo) item; - if (printer.getCapabilities() != null) { + if (printer.getCapabilities() != null + && printer.getStatus() != PrinterInfo.STATUS_UNAVAILABLE) { allOptionsEnabled = true; } } @@ -1470,14 +1588,14 @@ public class PrintJobConfigActivity extends Activity { // Media size if (mMediaSizeSpinner.getSelectedItemPosition() != AdapterView.INVALID_POSITION) { - mIgnoreNextMediaSizeChange = true; + mOldMediaSizeSelectionIndex = AdapterView.INVALID_POSITION; mMediaSizeSpinner.setSelection(AdapterView.INVALID_POSITION); } mMediaSizeSpinner.setEnabled(false); // Color mode if (mColorModeSpinner.getSelectedItemPosition() != AdapterView.INVALID_POSITION) { - mIgnoreNextColorModeChange = true; + mOldColorModeSelectionIndex = AdapterView.INVALID_POSITION; mColorModeSpinner.setSelection(AdapterView.INVALID_POSITION); } mColorModeSpinner.setEnabled(false); @@ -1513,9 +1631,9 @@ public class PrintJobConfigActivity extends Activity { } else { boolean someAttributeSelectionChanged = false; - PrintAttributes defaultAttributes = mTempPrintAttributes; PrinterInfo printer = (PrinterInfo) mDestinationSpinner.getSelectedItem(); PrinterCapabilitiesInfo capabilities = printer.getCapabilities(); + PrintAttributes defaultAttributes = mTempPrintAttributes; printer.getCapabilities().getDefaults(defaultAttributes); // Media size. @@ -1551,25 +1669,19 @@ public class PrintJobConfigActivity extends Activity { mediaSize, mediaSize.getLabel(getPackageManager()))); } - if (mediaSizeCount <= 0) { - // No media sizes - clear the selection. - mMediaSizeSpinner.setEnabled(false); - // Clear selection and mark if selection changed. - someAttributeSelectionChanged = setMediaSizeSpinnerSelectionNoCallback( - AdapterView.INVALID_POSITION); - } else { - mMediaSizeSpinner.setEnabled(true); + mMediaSizeSpinner.setEnabled(true); - if (oldMediaSizeNewIndex != AdapterView.INVALID_POSITION) { - // Select the old media size - nothing really changed. - setMediaSizeSpinnerSelectionNoCallback(oldMediaSizeNewIndex); - } else { - // Select the first or the default and mark if selection changed. - final int mediaSizeIndex = Math.max(mediaSizes.indexOf( - defaultAttributes.getMediaSize()), 0); - someAttributeSelectionChanged = setMediaSizeSpinnerSelectionNoCallback( - mediaSizeIndex); - } + if (oldMediaSizeNewIndex != AdapterView.INVALID_POSITION) { + // Select the old media size - nothing really changed. + setMediaSizeSpinnerSelectionNoCallback(oldMediaSizeNewIndex); + } else { + // Select the first or the default and mark if selection changed. + final int mediaSizeIndex = Math.max(mediaSizes.indexOf( + defaultAttributes.getMediaSize()), 0); + setMediaSizeSpinnerSelectionNoCallback(mediaSizeIndex); + mCurrPrintAttributes.setMediaSize(mMediaSizeSpinnerAdapter + .getItem(mediaSizeIndex).value); + someAttributeSelectionChanged = true; } } mMediaSizeSpinner.setEnabled(true); @@ -1618,26 +1730,32 @@ public class PrintJobConfigActivity extends Activity { mColorModeSpinnerAdapter.add(new SpinnerItem<Integer>(colorMode, colorModeLabels[colorBitOffset])); } - final int colorModeCount = Integer.bitCount(colorModes); - if (colorModeCount <= 0) { - mColorModeSpinner.setEnabled(false); - mColorModeSpinner.setSelection(AdapterView.INVALID_POSITION); + mColorModeSpinner.setEnabled(true); + if (oldColorModeNewIndex != AdapterView.INVALID_POSITION) { + // Select the old color mode - nothing really changed. + setColorModeSpinnerSelectionNoCallback(oldColorModeNewIndex); } else { - mColorModeSpinner.setEnabled(true); - if (oldColorModeNewIndex != AdapterView.INVALID_POSITION) { - // Select the old color mode - nothing really changed. - setColorModeSpinnerSelectionNoCallback(oldColorModeNewIndex); - } else { - final int selectedColorModeIndex = Integer.numberOfTrailingZeros( + final int selectedColorModeIndex = Integer.numberOfTrailingZeros( (colorModes & defaultAttributes.getColorMode())); - someAttributeSelectionChanged = setColorModeSpinnerSelectionNoCallback( - selectedColorModeIndex); - } + setColorModeSpinnerSelectionNoCallback(selectedColorModeIndex); + mCurrPrintAttributes.setColorMode(mColorModeSpinnerAdapter + .getItem(selectedColorModeIndex).value); + someAttributeSelectionChanged = true; } } mColorModeSpinner.setEnabled(true); // Orientation + MediaSize mediaSize = mCurrPrintAttributes.getMediaSize(); + if (mediaSize.isPortrait() + && mOrientationSpinner.getSelectedItemPosition() != 0) { + mIgnoreNextOrientationChange = true; + mOrientationSpinner.setSelection(0); + } else if (!mediaSize.isPortrait() + && mOrientationSpinner.getSelectedItemPosition() != 1) { + mIgnoreNextOrientationChange = true; + mOrientationSpinner.setSelection(1); + } mOrientationSpinner.setEnabled(true); // Range options @@ -1723,50 +1841,18 @@ public class PrintJobConfigActivity extends Activity { } } - private boolean setMediaSizeSpinnerSelectionNoCallback(int position) { + private void setMediaSizeSpinnerSelectionNoCallback(int position) { if (mMediaSizeSpinner.getSelectedItemPosition() != position) { - mIgnoreNextMediaSizeChange = true; + mOldMediaSizeSelectionIndex = position; mMediaSizeSpinner.setSelection(position); - return true; } - return false; } - private boolean setColorModeSpinnerSelectionNoCallback(int position) { + private void setColorModeSpinnerSelectionNoCallback(int position) { if (mColorModeSpinner.getSelectedItemPosition() != position) { - mIgnoreNextColorModeChange = true; + mOldColorModeSelectionIndex = position; mColorModeSpinner.setSelection(position); - return true; - } - return false; - } - - private void updateUiForNewPrinterCapabilities() { - // The printer changed so we want to start with a clean slate - // for the print options and let them be populated from the - // printer capabilities and use the printer defaults. - if (!mMediaSizeSpinnerAdapter.isEmpty()) { - mIgnoreNextMediaSizeChange = true; - mMediaSizeSpinnerAdapter.clear(); - } - if (!mColorModeSpinnerAdapter.isEmpty()) { - mIgnoreNextColorModeChange = true; - mColorModeSpinnerAdapter.clear(); - } - if (mOrientationSpinner.getSelectedItemPosition() != 0) { - mIgnoreNextOrientationChange = true; - mOrientationSpinner.setSelection(0); - } - if (mRangeOptionsSpinner.getSelectedItemPosition() != 0) { - mIgnoreNextRangeOptionChange = true; - mRangeOptionsSpinner.setSelection(0); - } - if (!TextUtils.isEmpty(mCopiesEditText.getText())) { - mIgnoreNextCopiesChange = true; - mCopiesEditText.setText(MIN_COPIES_STRING); } - - updateUi(); } private void startSelectPrinterActivity() { diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java b/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java index 7266af8..76e548a 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java +++ b/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java @@ -73,9 +73,9 @@ public final class PrintSpoolerService extends Service { private static final String LOG_TAG = "PrintSpoolerService"; - private static final boolean DEBUG_PRINT_JOB_LIFECYCLE = true; + private static final boolean DEBUG_PRINT_JOB_LIFECYCLE = false; - private static final boolean DEBUG_PERSISTENCE = true; + private static final boolean DEBUG_PERSISTENCE = false; private static final boolean PERSISTNECE_MANAGER_ENABLED = true; @@ -838,18 +838,8 @@ public final class PrintSpoolerService extends Service { resolution.getHorizontalDpi())); serializer.attribute(null, ATTR_VERTICAL_DPI, String.valueOf( resolution.getVerticalDpi())); - // We prefer to store only the package name and - // resource id and fallback to the label. - if (!TextUtils.isEmpty(mediaSize.mPackageName) - && resolution.mLabelResId > 0) { - serializer.attribute(null, ATTR_PACKAGE_NAME, - resolution.mPackageName); - serializer.attribute(null, ATTR_LABEL_RES_ID, - String.valueOf(resolution.mLabelResId)); - } else { - serializer.attribute(null, ATTR_LABEL, - resolution.getLabel(getPackageManager())); - } + serializer.attribute(null, ATTR_LABEL, + resolution.getLabel(getPackageManager())); serializer.endTag(null, TAG_RESOLUTION); } @@ -1047,11 +1037,7 @@ public final class PrintSpoolerService extends Service { ATTR_HORIZONTAL_DPI)); final int verticalDpi = Integer.parseInt(parser.getAttributeValue(null, ATTR_VERTICAL_DPI)); - String packageName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME); - final int labelResId = Integer.parseInt( - parser.getAttributeValue(null, ATTR_LABEL_RES_ID)); - Resolution resolution = new Resolution(id, label, packageName, labelResId, - horizontalDpi, verticalDpi); + Resolution resolution = new Resolution(id, label, horizontalDpi, verticalDpi); builder.setResolution(resolution); parser.next(); skipEmptyTextTags(parser); diff --git a/packages/PrintSpooler/src/com/android/printspooler/RemotePrintDocumentAdapter.java b/packages/PrintSpooler/src/com/android/printspooler/RemotePrintDocumentAdapter.java index 4006a5a..fd14af9 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/RemotePrintDocumentAdapter.java +++ b/packages/PrintSpooler/src/com/android/printspooler/RemotePrintDocumentAdapter.java @@ -17,7 +17,6 @@ package com.android.printspooler; import android.os.AsyncTask; -import android.os.Build; import android.os.Bundle; import android.os.ParcelFileDescriptor; import android.os.RemoteException; @@ -43,7 +42,7 @@ import java.io.OutputStream; final class RemotePrintDocumentAdapter { private static final String LOG_TAG = "RemotePrintDocumentAdapter"; - private static final boolean DEBUG = true && Build.IS_DEBUGGABLE; + private static final boolean DEBUG = false; private final IPrintDocumentAdapter mRemoteInterface; diff --git a/services/java/com/android/server/location/FlpHardwareProvider.java b/services/java/com/android/server/location/FlpHardwareProvider.java index 60893b8..fab84a8 100644 --- a/services/java/com/android/server/location/FlpHardwareProvider.java +++ b/services/java/com/android/server/location/FlpHardwareProvider.java @@ -26,6 +26,7 @@ import android.location.FusedBatchOptions; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; +import android.location.LocationRequest; import android.content.Context; import android.os.Bundle; @@ -73,10 +74,19 @@ public class FlpHardwareProvider { // register for listening for passive provider data LocationManager manager = (LocationManager) mContext.getSystemService( Context.LOCATION_SERVICE); - manager.requestLocationUpdates( + final long minTime = 0; + final float minDistance = 0; + final boolean oneShot = false; + LocationRequest request = LocationRequest.createFromDeprecatedProvider( LocationManager.PASSIVE_PROVIDER, - 0 /* minTime */, - 0 /* minDistance */, + minTime, + minDistance, + oneShot); + // Don't keep track of this request since it's done on behalf of other clients + // (which are kept track of separately). + request.setHideFromAppOps(true); + manager.requestLocationUpdates( + request, new NetworkLocationListener(), Looper.myLooper()); } diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java index 6053c61..9c76c19 100644 --- a/services/java/com/android/server/location/GpsLocationProvider.java +++ b/services/java/com/android/server/location/GpsLocationProvider.java @@ -512,8 +512,21 @@ public class GpsLocationProvider implements LocationProviderInterface { public void run() { LocationManager locManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE); - locManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER, - 0, 0, new NetworkLocationListener(), mHandler.getLooper()); + final long minTime = 0; + final float minDistance = 0; + final boolean oneShot = false; + LocationRequest request = LocationRequest.createFromDeprecatedProvider( + LocationManager.PASSIVE_PROVIDER, + minTime, + minDistance, + oneShot); + // Don't keep track of this request since it's done on behalf of other clients + // (which are kept track of separately). + request.setHideFromAppOps(true); + locManager.requestLocationUpdates( + request, + new NetworkLocationListener(), + mHandler.getLooper()); } }); } diff --git a/services/java/com/android/server/power/DisplayPowerController.java b/services/java/com/android/server/power/DisplayPowerController.java index b5010f2..976a328 100644 --- a/services/java/com/android/server/power/DisplayPowerController.java +++ b/services/java/com/android/server/power/DisplayPowerController.java @@ -29,7 +29,6 @@ import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; -import android.hardware.SystemSensorManager; import android.os.Handler; import android.os.Looper; import android.os.Message; @@ -119,7 +118,7 @@ final class DisplayPowerController { // Proximity sensor debounce delay in milliseconds for positive or negative transitions. private static final int PROXIMITY_SENSOR_POSITIVE_DEBOUNCE_DELAY = 0; - private static final int PROXIMITY_SENSOR_NEGATIVE_DEBOUNCE_DELAY = 500; + private static final int PROXIMITY_SENSOR_NEGATIVE_DEBOUNCE_DELAY = 250; // Trigger proximity if distance is less than 5 cm. private static final float TYPICAL_PROXIMITY_THRESHOLD = 5.0f; @@ -164,6 +163,10 @@ final class DisplayPowerController { // Notifier for sending asynchronous notifications. private final Notifier mNotifier; + // The display suspend blocker. + // Held while there are pending state change notifications. + private final SuspendBlocker mDisplaySuspendBlocker; + // The display blanker. private final DisplayBlanker mDisplayBlanker; @@ -271,7 +274,7 @@ final class DisplayPowerController { // The raw non-debounced proximity sensor state. private int mPendingProximity = PROXIMITY_UNKNOWN; - private long mPendingProximityDebounceTime; + private long mPendingProximityDebounceTime = -1; // -1 if fully debounced // True if the screen was turned off because of the proximity sensor. // When the screen turns on again, we report user activity to the power manager. @@ -346,10 +349,11 @@ final class DisplayPowerController { public DisplayPowerController(Looper looper, Context context, Notifier notifier, LightsService lights, TwilightService twilight, SensorManager sensorManager, DisplayManagerService displayManager, - DisplayBlanker displayBlanker, + SuspendBlocker displaySuspendBlocker, DisplayBlanker displayBlanker, Callbacks callbacks, Handler callbackHandler) { mHandler = new DisplayControllerHandler(looper); mNotifier = notifier; + mDisplaySuspendBlocker = displaySuspendBlocker; mDisplayBlanker = displayBlanker; mCallbacks = callbacks; mCallbackHandler = callbackHandler; @@ -601,7 +605,7 @@ final class DisplayPowerController { if (!mScreenOffBecauseOfProximity && mProximity == PROXIMITY_POSITIVE) { mScreenOffBecauseOfProximity = true; - sendOnProximityPositive(); + sendOnProximityPositiveWithWakelock(); setScreenOn(false); } } else if (mWaitingForNegativeProximity @@ -616,7 +620,7 @@ final class DisplayPowerController { if (mScreenOffBecauseOfProximity && mProximity != PROXIMITY_POSITIVE) { mScreenOffBecauseOfProximity = false; - sendOnProximityNegative(); + sendOnProximityNegativeWithWakelock(); } } else { mWaitingForNegativeProximity = false; @@ -737,7 +741,7 @@ final class DisplayPowerController { } } } - sendOnStateChanged(); + sendOnStateChangedWithWakelock(); } } @@ -810,49 +814,67 @@ final class DisplayPowerController { private void setProximitySensorEnabled(boolean enable) { if (enable) { if (!mProximitySensorEnabled) { + // Register the listener. + // Proximity sensor state already cleared initially. mProximitySensorEnabled = true; - mPendingProximity = PROXIMITY_UNKNOWN; mSensorManager.registerListener(mProximitySensorListener, mProximitySensor, SensorManager.SENSOR_DELAY_NORMAL, mHandler); } } else { if (mProximitySensorEnabled) { + // Unregister the listener. + // Clear the proximity sensor state for next time. mProximitySensorEnabled = false; mProximity = PROXIMITY_UNKNOWN; + mPendingProximity = PROXIMITY_UNKNOWN; mHandler.removeMessages(MSG_PROXIMITY_SENSOR_DEBOUNCED); mSensorManager.unregisterListener(mProximitySensorListener); + clearPendingProximityDebounceTime(); // release wake lock (must be last) } } } private void handleProximitySensorEvent(long time, boolean positive) { - if (mPendingProximity == PROXIMITY_NEGATIVE && !positive) { - return; // no change - } - if (mPendingProximity == PROXIMITY_POSITIVE && positive) { - return; // no change - } + if (mProximitySensorEnabled) { + if (mPendingProximity == PROXIMITY_NEGATIVE && !positive) { + return; // no change + } + if (mPendingProximity == PROXIMITY_POSITIVE && positive) { + return; // no change + } - // Only accept a proximity sensor reading if it remains - // stable for the entire debounce delay. - mHandler.removeMessages(MSG_PROXIMITY_SENSOR_DEBOUNCED); - if (positive) { - mPendingProximity = PROXIMITY_POSITIVE; - mPendingProximityDebounceTime = time + PROXIMITY_SENSOR_POSITIVE_DEBOUNCE_DELAY; - } else { - mPendingProximity = PROXIMITY_NEGATIVE; - mPendingProximityDebounceTime = time + PROXIMITY_SENSOR_NEGATIVE_DEBOUNCE_DELAY; + // Only accept a proximity sensor reading if it remains + // stable for the entire debounce delay. We hold a wake lock while + // debouncing the sensor. + mHandler.removeMessages(MSG_PROXIMITY_SENSOR_DEBOUNCED); + if (positive) { + mPendingProximity = PROXIMITY_POSITIVE; + setPendingProximityDebounceTime( + time + PROXIMITY_SENSOR_POSITIVE_DEBOUNCE_DELAY); // acquire wake lock + } else { + mPendingProximity = PROXIMITY_NEGATIVE; + setPendingProximityDebounceTime( + time + PROXIMITY_SENSOR_NEGATIVE_DEBOUNCE_DELAY); // acquire wake lock + } + + // Debounce the new sensor reading. + debounceProximitySensor(); } - debounceProximitySensor(); } private void debounceProximitySensor() { - if (mPendingProximity != PROXIMITY_UNKNOWN) { + if (mProximitySensorEnabled + && mPendingProximity != PROXIMITY_UNKNOWN + && mPendingProximityDebounceTime >= 0) { final long now = SystemClock.uptimeMillis(); if (mPendingProximityDebounceTime <= now) { + // Sensor reading accepted. Apply the change then release the wake lock. mProximity = mPendingProximity; - sendUpdatePowerState(); + updatePowerState(); + clearPendingProximityDebounceTime(); // release wake lock (must be last) } else { + // Need to wait a little longer. + // Debounce again later. We continue holding a wake lock while waiting. Message msg = mHandler.obtainMessage(MSG_PROXIMITY_SENSOR_DEBOUNCED); msg.setAsynchronous(true); mHandler.sendMessageAtTime(msg, mPendingProximityDebounceTime); @@ -860,6 +882,20 @@ final class DisplayPowerController { } } + private void clearPendingProximityDebounceTime() { + if (mPendingProximityDebounceTime >= 0) { + mPendingProximityDebounceTime = -1; + mDisplaySuspendBlocker.release(); // release wake lock + } + } + + private void setPendingProximityDebounceTime(long debounceTime) { + if (mPendingProximityDebounceTime < 0) { + mDisplaySuspendBlocker.acquire(); // acquire wake lock + } + mPendingProximityDebounceTime = debounceTime; + } + private void setLightSensorEnabled(boolean enable, boolean updateAutoBrightness) { if (enable) { if (!mLightSensorEnabled) { @@ -1120,7 +1156,8 @@ final class DisplayPowerController { return x + (y - x) * alpha; } - private void sendOnStateChanged() { + private void sendOnStateChangedWithWakelock() { + mDisplaySuspendBlocker.acquire(); mCallbackHandler.post(mOnStateChangedRunnable); } @@ -1128,10 +1165,12 @@ final class DisplayPowerController { @Override public void run() { mCallbacks.onStateChanged(); + mDisplaySuspendBlocker.release(); } }; - private void sendOnProximityPositive() { + private void sendOnProximityPositiveWithWakelock() { + mDisplaySuspendBlocker.acquire(); mCallbackHandler.post(mOnProximityPositiveRunnable); } @@ -1139,10 +1178,12 @@ final class DisplayPowerController { @Override public void run() { mCallbacks.onProximityPositive(); + mDisplaySuspendBlocker.release(); } }; - private void sendOnProximityNegative() { + private void sendOnProximityNegativeWithWakelock() { + mDisplaySuspendBlocker.acquire(); mCallbackHandler.post(mOnProximityNegativeRunnable); } @@ -1150,6 +1191,7 @@ final class DisplayPowerController { @Override public void run() { mCallbacks.onProximityNegative(); + mDisplaySuspendBlocker.release(); } }; diff --git a/services/java/com/android/server/power/PowerManagerService.java b/services/java/com/android/server/power/PowerManagerService.java index 2167f67..fe09a33 100644 --- a/services/java/com/android/server/power/PowerManagerService.java +++ b/services/java/com/android/server/power/PowerManagerService.java @@ -63,7 +63,6 @@ import android.util.TimeUtils; import android.view.WindowManagerPolicy; import java.io.FileDescriptor; -import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; @@ -287,6 +286,9 @@ public final class PowerManagerService extends IPowerManager.Stub // True if the device should wake up when plugged or unplugged. private boolean mWakeUpWhenPluggedOrUnpluggedConfig; + // True if the device should suspend when the screen is off due to proximity. + private boolean mSuspendWhenScreenOffDueToProximityConfig; + // True if dreams are supported on this device. private boolean mDreamsSupportedConfig; @@ -447,7 +449,7 @@ public final class PowerManagerService extends IPowerManager.Stub // own handler thread to ensure timely operation. mDisplayPowerController = new DisplayPowerController(mHandler.getLooper(), mContext, mNotifier, mLightsService, twilight, sensorManager, - mDisplayManagerService, mDisplayBlanker, + mDisplayManagerService, mDisplaySuspendBlocker, mDisplayBlanker, mDisplayPowerControllerCallbacks, mHandler); mWirelessChargerDetector = new WirelessChargerDetector(sensorManager, @@ -514,6 +516,8 @@ public final class PowerManagerService extends IPowerManager.Stub mWakeUpWhenPluggedOrUnpluggedConfig = resources.getBoolean( com.android.internal.R.bool.config_unplugTurnsOnScreen); + mSuspendWhenScreenOffDueToProximityConfig = resources.getBoolean( + com.android.internal.R.bool.config_suspendWhenScreenOffDueToProximity); mDreamsSupportedConfig = resources.getBoolean( com.android.internal.R.bool.config_dreamsSupported); mDreamsEnabledByDefaultConfig = resources.getBoolean( @@ -639,6 +643,7 @@ public final class PowerManagerService extends IPowerManager.Stub } } + @SuppressWarnings("deprecation") private static boolean isScreenLock(final WakeLock wakeLock) { switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) { case PowerManager.FULL_WAKE_LOCK: @@ -817,6 +822,7 @@ public final class PowerManagerService extends IPowerManager.Stub } } + @SuppressWarnings("deprecation") private boolean isWakeLockLevelSupportedInternal(int level) { synchronized (mLock) { switch (level) { @@ -1005,6 +1011,7 @@ public final class PowerManagerService extends IPowerManager.Stub } } + @SuppressWarnings("deprecation") private boolean goToSleepNoUpdateLocked(long eventTime, int reason) { if (DEBUG_SPEW) { Slog.d(TAG, "goToSleepNoUpdateLocked: eventTime=" + eventTime + ", reason=" + reason); @@ -1261,6 +1268,7 @@ public final class PowerManagerService extends IPowerManager.Stub * * This function must have no other side-effects. */ + @SuppressWarnings("deprecation") private void updateWakeLockSummaryLocked(int dirty) { if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_WAKEFULNESS)) != 0) { mWakeLockSummary = 0; @@ -1299,7 +1307,7 @@ public final class PowerManagerService extends IPowerManager.Stub break; case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK: if (mWakefulness != WAKEFULNESS_ASLEEP) { - mWakeLockSummary |= WAKE_LOCK_CPU | WAKE_LOCK_PROXIMITY_SCREEN_OFF; + mWakeLockSummary |= WAKE_LOCK_PROXIMITY_SCREEN_OFF; } break; } @@ -1458,7 +1466,11 @@ public final class PowerManagerService extends IPowerManager.Stub /** * Returns true if the device is being kept awake by a wake lock, user activity - * or the stay on while powered setting. + * or the stay on while powered setting. We also keep the phone awake when + * the proximity sensor returns a positive result so that the device does not + * lock while in a phone call. This function only controls whether the device + * will go to sleep or dream which is independent of whether it will be allowed + * to suspend. */ private boolean isBeingKeptAwakeLocked() { return mStayOn @@ -1749,10 +1761,8 @@ public final class PowerManagerService extends IPowerManager.Stub * This function must have no other side-effects. */ private void updateSuspendBlockerLocked() { - final boolean needWakeLockSuspendBlocker = (mWakeLockSummary != 0); - final boolean needDisplaySuspendBlocker = (mUserActivitySummary != 0 - || mDisplayPowerRequest.screenState != DisplayPowerRequest.SCREEN_STATE_OFF - || !mDisplayReady || !mBootCompleted); + final boolean needWakeLockSuspendBlocker = ((mWakeLockSummary & WAKE_LOCK_CPU) != 0); + final boolean needDisplaySuspendBlocker = needDisplaySuspendBlocker(); // First acquire suspend blockers if needed. if (needWakeLockSuspendBlocker && !mHoldingWakeLockSuspendBlocker) { @@ -1775,6 +1785,27 @@ public final class PowerManagerService extends IPowerManager.Stub } } + /** + * Return true if we must keep a suspend blocker active on behalf of the display. + * We do so if the screen is on or is in transition between states. + */ + private boolean needDisplaySuspendBlocker() { + if (!mDisplayReady) { + return true; + } + if (mDisplayPowerRequest.screenState != DisplayPowerRequest.SCREEN_STATE_OFF) { + // If we asked for the screen to be on but it is off due to the proximity + // sensor then we may suspend but only if the configuration allows it. + // On some hardware it may not be safe to suspend because the proximity + // sensor may not be correctly configured as a wake-up source. + if (!mDisplayPowerRequest.useProximitySensor || !mProximityPositive + || !mSuspendWhenScreenOffDueToProximityConfig) { + return true; + } + } + return false; + } + @Override // Binder call public boolean isScreenOn() { final long ident = Binder.clearCallingIdentity(); @@ -2115,7 +2146,7 @@ public final class PowerManagerService extends IPowerManager.Stub * * @param brightness The overridden brightness. * - * @see Settings.System#SCREEN_BRIGHTNESS + * @see android.provider.Settings.System#SCREEN_BRIGHTNESS */ @Override // Binder call public void setTemporaryScreenBrightnessSettingOverride(int brightness) { @@ -2255,7 +2286,16 @@ public final class PowerManagerService extends IPowerManager.Stub pw.println(); pw.println("Settings and Configuration:"); + pw.println(" mWakeUpWhenPluggedOrUnpluggedConfig=" + + mWakeUpWhenPluggedOrUnpluggedConfig); + pw.println(" mSuspendWhenScreenOffDueToProximityConfig=" + + mSuspendWhenScreenOffDueToProximityConfig); pw.println(" mDreamsSupportedConfig=" + mDreamsSupportedConfig); + pw.println(" mDreamsEnabledByDefaultConfig=" + mDreamsEnabledByDefaultConfig); + pw.println(" mDreamsActivatedOnSleepByDefaultConfig=" + + mDreamsActivatedOnSleepByDefaultConfig); + pw.println(" mDreamsActivatedOnDockByDefaultConfig=" + + mDreamsActivatedOnDockByDefaultConfig); pw.println(" mDreamsEnabledSetting=" + mDreamsEnabledSetting); pw.println(" mDreamsActivateOnSleepSetting=" + mDreamsActivateOnSleepSetting); pw.println(" mDreamsActivateOnDockSetting=" + mDreamsActivateOnDockSetting); diff --git a/services/java/com/android/server/print/RemotePrintService.java b/services/java/com/android/server/print/RemotePrintService.java index ddff0ae..f15c760 100644 --- a/services/java/com/android/server/print/RemotePrintService.java +++ b/services/java/com/android/server/print/RemotePrintService.java @@ -21,11 +21,9 @@ import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Binder; -import android.os.Build; import android.os.Handler; import android.os.IBinder; import android.os.IBinder.DeathRecipient; -import android.os.AsyncTask; import android.os.Looper; import android.os.Message; import android.os.ParcelFileDescriptor; @@ -39,8 +37,6 @@ import android.printservice.IPrintService; import android.printservice.IPrintServiceClient; import android.util.Slog; -import com.android.internal.R; - import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.ArrayList; @@ -55,7 +51,7 @@ final class RemotePrintService implements DeathRecipient { private static final String LOG_TAG = "RemotePrintService"; - private static final boolean DEBUG = true && Build.IS_DEBUGGABLE; + private static final boolean DEBUG = false; private final Context mContext; @@ -65,7 +61,7 @@ final class RemotePrintService implements DeathRecipient { private final RemotePrintSpooler mSpooler; - private final UserState mUserState; + private final PrintServiceCallbacks mCallbacks; private final int mUserId; @@ -83,14 +79,24 @@ final class RemotePrintService implements DeathRecipient { private boolean mDestroyed; - private boolean mAllPrintJobsHandled; + private boolean mHasActivePrintJobs; private boolean mHasPrinterDiscoverySession; + private List<PrinterId> mDiscoveryPriorityList; + + private List<PrinterId> mTrackedPrinterList; + + public static interface PrintServiceCallbacks { + public void onPrintersAdded(List<PrinterInfo> printers); + public void onPrintersRemoved(List<PrinterId> printerIds); + public void onServiceDied(RemotePrintService service); + } + public RemotePrintService(Context context, ComponentName componentName, int userId, - RemotePrintSpooler spooler, UserState userState) { + RemotePrintSpooler spooler, PrintServiceCallbacks callbacks) { mContext = context; - mUserState = userState; + mCallbacks = callbacks; mComponentName = componentName; mIntent = new Intent().setComponent(mComponentName); mUserId = userId; @@ -99,15 +105,42 @@ final class RemotePrintService implements DeathRecipient { mPrintServiceClient = new RemotePrintServiceClient(this); } + public ComponentName getComponentName() { + return mComponentName; + } + public void destroy() { mHandler.sendEmptyMessage(MyHandler.MSG_DESTROY); } private void handleDestroy() { throwIfDestroyed(); + + // Stop tracking printers. + if (mTrackedPrinterList != null) { + final int trackedPrinterCount = mTrackedPrinterList.size(); + for (int i = 0; i < trackedPrinterCount; i++) { + PrinterId printerId = mTrackedPrinterList.get(i); + if (printerId.getServiceName().equals(mComponentName)) { + handleStopPrinterStateTracking(printerId); + } + } + } + + // Stop printer discovery. + if (mDiscoveryPriorityList != null) { + handleStopPrinterDiscovery(); + } + + // Destroy the discovery session. + if (mHasPrinterDiscoverySession) { + handleDestroyPrinterDiscoverySession(); + } + + // Unbind. ensureUnbound(); - mAllPrintJobsHandled = false; - mHasPrinterDiscoverySession = false; + + // Done mDestroyed = true; } @@ -121,21 +154,9 @@ final class RemotePrintService implements DeathRecipient { } private void handleBinderDied() { - mAllPrintJobsHandled = false; - mHasPrinterDiscoverySession = false; - mPendingCommands.clear(); - ensureUnbound(); - - // Makes sure all active print jobs are failed since the service - // just died. Do this off the main thread since we do to allow - // calls into the spooler on the main thread. - new AsyncTask<Void, Void, Void>() { - @Override - protected Void doInBackground(Void... params) { - failAllActivePrintJobs(); - return null; - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null); + mPrintService.asBinder().unlinkToDeath(this, 0); + mPrintService = null; + mCallbacks.onServiceDied(this); } public void dump(PrintWriter pw, String prefix) { @@ -148,32 +169,17 @@ final class RemotePrintService implements DeathRecipient { pw.append(prefix).append(tab).append("bound=") .append(String.valueOf(isBound())).println(); pw.append(prefix).append(tab).append("hasDicoverySession=") - .append(String.valueOf(mHasPrinterDiscoverySession)); - } - - private void failAllActivePrintJobs() { - List<PrintJobInfo> printJobs = mSpooler.getPrintJobInfos(mComponentName, - PrintJobInfo.STATE_ANY_ACTIVE, PrintManager.APP_ID_ANY); - if (printJobs == null) { - return; - } - final long identity = Binder.clearCallingIdentity(); - try { - final int printJobCount = printJobs.size(); - for (int i = 0; i < printJobCount; i++) { - PrintJobInfo printJob = printJobs.get(i); - mSpooler.setPrintJobState(printJob.getId(), PrintJobInfo.STATE_FAILED, - mContext.getString(R.string.reason_unknown)); - } - } finally { - Binder.restoreCallingIdentity(identity); - } + .append(String.valueOf(mHasPrinterDiscoverySession)).println(); + pw.append(prefix).append(tab).append("isDiscoveringPrinters=") + .append(String.valueOf(mDiscoveryPriorityList != null)).println(); + pw.append(prefix).append(tab).append("trackedPrinters=") + .append((mTrackedPrinterList != null) ? mTrackedPrinterList.toString() : "null"); } private void handleOnAllPrintJobsHandled() { throwIfDestroyed(); - mAllPrintJobsHandled = true; + mHasActivePrintJobs = false; if (isBound()) { if (DEBUG) { @@ -217,7 +223,7 @@ final class RemotePrintService implements DeathRecipient { private void handleOnPrintJobQueued(final PrintJobInfo printJob) { throwIfDestroyed(); - mAllPrintJobsHandled = false; + mHasActivePrintJobs = true; if (!isBound()) { ensureBound(); @@ -296,7 +302,7 @@ final class RemotePrintService implements DeathRecipient { // If the service has no print jobs and no active discovery // session anymore we should disconnect from it. - if (mAllPrintJobsHandled) { + if (!mHasActivePrintJobs) { ensureUnbound(); } } @@ -326,6 +332,11 @@ final class RemotePrintService implements DeathRecipient { } catch (RemoteException re) { Slog.e(LOG_TAG, "Error starting printer dicovery.", re); } + // Take a note that we are doing discovery. + mDiscoveryPriorityList = new ArrayList<PrinterId>(); + if (priorityList != null) { + mDiscoveryPriorityList.addAll(priorityList); + } } } @@ -347,6 +358,8 @@ final class RemotePrintService implements DeathRecipient { if (DEBUG) { Slog.i(LOG_TAG, "[user: " + mUserId + "] stopPrinterDiscovery()"); } + // We are not doing discovery anymore. + mDiscoveryPriorityList = null; try { mPrintService.stopPrinterDiscovery(); } catch (RemoteException re) { @@ -406,6 +419,11 @@ final class RemotePrintService implements DeathRecipient { } catch (RemoteException re) { Slog.e(LOG_TAG, "Error requesting start printer tracking.", re); } + // Take a note we are tracking the printer. + if (mTrackedPrinterList == null) { + mTrackedPrinterList = new ArrayList<PrinterId>(); + } + mTrackedPrinterList.add(printerId); } } @@ -428,6 +446,13 @@ final class RemotePrintService implements DeathRecipient { if (DEBUG) { Slog.i(LOG_TAG, "[user: " + mUserId + "] stopPrinterTracking()"); } + // We are no longer tracking the printer. + if (mTrackedPrinterList == null || !mTrackedPrinterList.remove(printerId)) { + return; + } + if (mTrackedPrinterList.isEmpty()) { + mTrackedPrinterList = null; + } try { mPrintService.stopPrinterStateTracking(printerId); } catch (RemoteException re) { @@ -461,6 +486,10 @@ final class RemotePrintService implements DeathRecipient { } mBinding = false; mPendingCommands.clear(); + mHasActivePrintJobs = false; + mHasPrinterDiscoverySession = false; + mDiscoveryPriorityList = null; + mTrackedPrinterList = null; if (isBound()) { try { mPrintService.setClient(null); @@ -500,11 +529,31 @@ final class RemotePrintService implements DeathRecipient { handleBinderDied(); return; } + // If there is a session, then the service died after creating + // a session. Hence, recreate the session. + if (mHasPrinterDiscoverySession) { + handleCreatePrinterDiscoverySession(); + } + // If there is a priority list, then the service died during + // discovery and is restarted. Hence, start discovery. + if (mDiscoveryPriorityList != null) { + handleStartPrinterDiscovery(mDiscoveryPriorityList); + } + // If there is a tracked printer list, then the service died + // during discovery and is restarted. Hence, start tracking. + if (mTrackedPrinterList != null) { + final int trackedPrinterCount = mTrackedPrinterList.size(); + for (int i = 0; i < trackedPrinterCount; i++) { + handleStartPrinterStateTracking(mTrackedPrinterList.get(i)); + } + } + // Finally, do all the pending work. final int pendingCommandCount = mPendingCommands.size(); for (int i = 0; i < pendingCommandCount; i++) { Runnable pendingCommand = mPendingCommands.get(i); pendingCommand.run(); } + mPendingCommands.clear(); } @Override @@ -677,7 +726,7 @@ final class RemotePrintService implements DeathRecipient { throwIfPrinterIdsForPrinterInfoTampered(service.mComponentName, printers); final long identity = Binder.clearCallingIdentity(); try { - service.mUserState.onPrintersAdded(printers); + service.mCallbacks.onPrintersAdded(printers); } finally { Binder.restoreCallingIdentity(identity); } @@ -691,7 +740,7 @@ final class RemotePrintService implements DeathRecipient { throwIfPrinterIdsTampered(service.mComponentName, printerIds); final long identity = Binder.clearCallingIdentity(); try { - service.mUserState.onPrintersRemoved(printerIds); + service.mCallbacks.onPrintersRemoved(printerIds); } finally { Binder.restoreCallingIdentity(identity); } diff --git a/services/java/com/android/server/print/RemotePrintSpooler.java b/services/java/com/android/server/print/RemotePrintSpooler.java index 28a6186..45469ac 100644 --- a/services/java/com/android/server/print/RemotePrintSpooler.java +++ b/services/java/com/android/server/print/RemotePrintSpooler.java @@ -21,7 +21,6 @@ import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Binder; -import android.os.Build; import android.os.IBinder; import android.os.ParcelFileDescriptor; import android.os.RemoteException; @@ -57,7 +56,7 @@ final class RemotePrintSpooler { private static final String LOG_TAG = "RemotePrintSpooler"; - private static final boolean DEBUG = true && Build.IS_DEBUGGABLE; + private static final boolean DEBUG = false; private static final long BIND_SPOOLER_SERVICE_TIMEOUT = 10000; diff --git a/services/java/com/android/server/print/UserState.java b/services/java/com/android/server/print/UserState.java index 5392975..70fe370 100644 --- a/services/java/com/android/server/print/UserState.java +++ b/services/java/com/android/server/print/UserState.java @@ -23,7 +23,8 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; -import android.os.Build; +import android.os.AsyncTask; +import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -32,6 +33,7 @@ import android.os.RemoteCallbackList; import android.os.RemoteException; import android.print.IPrinterDiscoveryObserver; import android.print.PrintJobInfo; +import android.print.PrintManager; import android.print.PrinterId; import android.print.PrinterInfo; import android.printservice.PrintServiceInfo; @@ -43,7 +45,9 @@ import android.util.ArraySet; import android.util.Log; import android.util.Slog; +import com.android.internal.R; import com.android.internal.os.SomeArgs; +import com.android.server.print.RemotePrintService.PrintServiceCallbacks; import com.android.server.print.RemotePrintSpooler.PrintSpoolerCallbacks; import java.io.FileDescriptor; @@ -58,11 +62,11 @@ import java.util.Set; /** * Represents the print state for a user. */ -final class UserState implements PrintSpoolerCallbacks { +final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { private static final String LOG_TAG = "UserState"; - private static final boolean DEBUG = true && Build.IS_DEBUGGABLE; + private static final boolean DEBUG = false; private static final int MAX_ITEMS_PER_CALLBACK = 50; @@ -246,6 +250,7 @@ final class UserState implements PrintSpoolerCallbacks { } } + @Override public void onPrintersAdded(List<PrinterInfo> printers) { synchronized (mLock) { throwIfDestroyedLocked(); @@ -257,11 +262,11 @@ final class UserState implements PrintSpoolerCallbacks { if (mPrinterDiscoverySession == null) { return; } - // Request an updated. mPrinterDiscoverySession.onPrintersAddedLocked(printers); } } + @Override public void onPrintersRemoved(List<PrinterId> printerIds) { synchronized (mLock) { throwIfDestroyedLocked(); @@ -273,11 +278,28 @@ final class UserState implements PrintSpoolerCallbacks { if (mPrinterDiscoverySession == null) { return; } - // Request an updated. mPrinterDiscoverySession.onPrintersRemovedLocked(printerIds); } } + @Override + public void onServiceDied(RemotePrintService service) { + synchronized (mLock) { + throwIfDestroyedLocked(); + // No services - nothing to do. + if (mActiveServices.isEmpty()) { + return; + } + // Fail all print jobs. + failActivePrintJobsForService(service.getComponentName()); + // No session - nothing to do. + if (mPrinterDiscoverySession == null) { + return; + } + mPrinterDiscoverySession.onServiceDiedLocked(service); + } + } + public void updateIfNeededLocked() { throwIfDestroyedLocked(); if (readConfigurationLocked()) { @@ -516,23 +538,72 @@ final class UserState implements PrintSpoolerCallbacks { if (!mActiveServices.containsKey(serviceName)) { RemotePrintService service = new RemotePrintService( mContext, serviceName, mUserId, mSpooler, this); - mActiveServices.put(serviceName, service); - if (mPrinterDiscoverySession != null) { - mPrinterDiscoverySession.onServiceAddedLocked(service); - } + addServiceLocked(service); } } else { RemotePrintService service = mActiveServices.remove(serviceName); if (service != null) { - service.destroy(); - if (mPrinterDiscoverySession != null) { - mPrinterDiscoverySession.onServiceRemovedLocked(serviceName); - } + removeServiceLocked(service); } } } } + private void addServiceLocked(RemotePrintService service) { + mActiveServices.put(service.getComponentName(), service); + if (mPrinterDiscoverySession != null) { + mPrinterDiscoverySession.onServiceAddedLocked(service); + } + } + + private void removeServiceLocked(RemotePrintService service) { + // Fail all print jobs. + failActivePrintJobsForService(service.getComponentName()); + // If discovery is in progress, tear down the service. + if (mPrinterDiscoverySession != null) { + mPrinterDiscoverySession.onServiceRemovedLocked(service); + } else { + // Otherwise, just destroy it. + service.destroy(); + } + } + + private void failActivePrintJobsForService(final ComponentName serviceName) { + // Makes sure all active print jobs are failed since the service + // just died. Do this off the main thread since we do to allow + // calls into the spooler on the main thread. + if (Looper.getMainLooper().isCurrentThread()) { + new AsyncTask<Void, Void, Void>() { + @Override + protected Void doInBackground(Void... params) { + failActivePrintJobsForServiceInternal(serviceName); + return null; + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null); + } else { + failActivePrintJobsForServiceInternal(serviceName); + } + } + + private void failActivePrintJobsForServiceInternal(ComponentName serviceName) { + List<PrintJobInfo> printJobs = mSpooler.getPrintJobInfos(serviceName, + PrintJobInfo.STATE_ANY_ACTIVE, PrintManager.APP_ID_ANY); + if (printJobs == null) { + return; + } + final long identity = Binder.clearCallingIdentity(); + try { + final int printJobCount = printJobs.size(); + for (int i = 0; i < printJobCount; i++) { + PrintJobInfo printJob = printJobs.get(i); + mSpooler.setPrintJobState(printJob.getId(), PrintJobInfo.STATE_FAILED, + mContext.getString(R.string.reason_unknown)); + } + } finally { + Binder.restoreCallingIdentity(identity); + } + } + private void throwIfDestroyedLocked() { if (mDestroyed) { throw new IllegalStateException("Cannot interact with a destroyed instance."); @@ -603,17 +674,18 @@ final class UserState implements PrintSpoolerCallbacks { return; } + final boolean discoveryStarted = !mStartedPrinterDiscoveryTokens.isEmpty(); + + // Remember we got a start request to match with an end. + mStartedPrinterDiscoveryTokens.add(observer.asBinder()); + // If printer discovery is ongoing and the start request has a list // of printer to be checked, then we just request validating them. - if (!mStartedPrinterDiscoveryTokens.isEmpty() - && priorityList != null && !priorityList.isEmpty()) { + if (discoveryStarted && priorityList != null && !priorityList.isEmpty()) { validatePrinters(priorityList); return; } - // Remember we got a start request to match with an end. - mStartedPrinterDiscoveryTokens.add(observer.asBinder()); - // The service are already performing discovery - nothing to do. if (mStartedPrinterDiscoveryTokens.size() > 1) { return; @@ -822,32 +894,20 @@ final class UserState implements PrintSpoolerCallbacks { } } - public void onServiceRemovedLocked(ComponentName serviceName) { + public void onServiceRemovedLocked(RemotePrintService service) { if (mIsDestroyed) { Log.w(LOG_TAG, "Not updating removed service - session destroyed"); return; } - // No printers - nothing to do. - if (mPrinters.isEmpty()) { - return; - } - // Remove the printers for that service. - List<PrinterId> removedPrinterIds = null; - final int printerCount = mPrinters.size(); - for (int i = 0; i < printerCount; i++) { - PrinterId printerId = mPrinters.keyAt(i); - if (printerId.getServiceName().equals(serviceName)) { - if (removedPrinterIds == null) { - removedPrinterIds = new ArrayList<PrinterId>(); - } - removedPrinterIds.add(printerId); - } - } - if (!removedPrinterIds.isEmpty()) { - mHandler.obtainMessage( - SessionHandler.MSG_DISPATCH_PRINTERS_REMOVED, - removedPrinterIds).sendToTarget(); - } + // Remove the reported and tracked printers for that service. + ComponentName serviceName = service.getComponentName(); + removePrintersForServiceLocked(serviceName); + service.destroy(); + } + + public void onServiceDiedLocked(RemotePrintService service) { + // Remove the reported by that service. + removePrintersForServiceLocked(service.getComponentName()); } public void onServiceAddedLocked(RemotePrintService service) { @@ -908,6 +968,34 @@ final class UserState implements PrintSpoolerCallbacks { } } + private void removePrintersForServiceLocked(ComponentName serviceName) { + // No printers - nothing to do. + if (mPrinters.isEmpty()) { + return; + } + // Remove the printers for that service. + List<PrinterId> removedPrinterIds = null; + final int printerCount = mPrinters.size(); + for (int i = 0; i < printerCount; i++) { + PrinterId printerId = mPrinters.keyAt(i); + if (printerId.getServiceName().equals(serviceName)) { + if (removedPrinterIds == null) { + removedPrinterIds = new ArrayList<PrinterId>(); + } + removedPrinterIds.add(printerId); + } + } + final int removedPrinterCount = removedPrinterIds.size(); + for (int i = 0; i < removedPrinterCount; i++) { + mPrinters.remove(removedPrinterIds.get(i)); + } + if (removedPrinterIds != null) { + mHandler.obtainMessage( + SessionHandler.MSG_DISPATCH_PRINTERS_REMOVED, + removedPrinterIds).sendToTarget(); + } + } + private void handleDispatchPrintersAdded(List<PrinterInfo> addedPrinters) { final int observerCount = mDiscoveryObservers.beginBroadcast(); for (int i = 0; i < observerCount; i++) { @@ -1026,14 +1114,17 @@ final class UserState implements PrintSpoolerCallbacks { public static final int MSG_DISPATCH_PRINTERS_REMOVED = 4; public static final int MSG_CREATE_PRINTER_DISCOVERY_SESSION = 5; - public static final int MSG_START_PRINTER_DISCOVERY = 6; - public static final int MSG_DISPATCH_CREATE_PRINTER_DISCOVERY_SESSION = 7; - public static final int MSG_DISPATCH_DESTROY_PRINTER_DISCOVERY_SESSION = 8; - public static final int MSG_DISPATCH_START_PRINTER_DISCOVERY = 9; - public static final int MSG_DISPATCH_STOP_PRINTER_DISCOVERY = 10; - public static final int MSG_VALIDATE_PRINTERS = 11; - public static final int MSG_START_PRINTER_STATE_TRACKING = 12; - public static final int MSG_STOP_PRINTER_STATE_TRACKING = 13; + public static final int MSG_DESTROY_PRINTER_DISCOVERY_SESSION = 6; + public static final int MSG_START_PRINTER_DISCOVERY = 7; + public static final int MSG_STOP_PRINTER_DISCOVERY = 8; + public static final int MSG_DISPATCH_CREATE_PRINTER_DISCOVERY_SESSION = 9; + public static final int MSG_DISPATCH_DESTROY_PRINTER_DISCOVERY_SESSION = 10; + public static final int MSG_DISPATCH_START_PRINTER_DISCOVERY = 11; + public static final int MSG_DISPATCH_STOP_PRINTER_DISCOVERY = 12; + public static final int MSG_VALIDATE_PRINTERS = 13; + public static final int MSG_START_PRINTER_STATE_TRACKING = 14; + public static final int MSG_STOP_PRINTER_STATE_TRACKING = 15; + public static final int MSG_DESTROY_SERVICE = 16; SessionHandler(Looper looper) { super(looper, null, false); @@ -1074,11 +1165,21 @@ final class UserState implements PrintSpoolerCallbacks { service.createPrinterDiscoverySession(); } break; + case MSG_DESTROY_PRINTER_DISCOVERY_SESSION: { + RemotePrintService service = (RemotePrintService) message.obj; + service.destroyPrinterDiscoverySession(); + } break; + case MSG_START_PRINTER_DISCOVERY: { RemotePrintService service = (RemotePrintService) message.obj; service.startPrinterDiscovery(null); } break; + case MSG_STOP_PRINTER_DISCOVERY: { + RemotePrintService service = (RemotePrintService) message.obj; + service.stopPrinterDiscovery(); + } break; + case MSG_DISPATCH_CREATE_PRINTER_DISCOVERY_SESSION: { List<RemotePrintService> services = (List<RemotePrintService>) message.obj; handleDispatchCreatePrinterDiscoverySession(services); @@ -1124,7 +1225,12 @@ final class UserState implements PrintSpoolerCallbacks { PrinterId printerId = (PrinterId) args.arg2; args.recycle(); handleStopPrinterStateTracking(service, printerId); - } + } break; + + case MSG_DESTROY_SERVICE: { + RemotePrintService service = (RemotePrintService) message.obj; + service.destroy(); + } break; } } } diff --git a/services/java/com/android/server/wifi/WifiService.java b/services/java/com/android/server/wifi/WifiService.java index ed2b8fd..46f100e 100644 --- a/services/java/com/android/server/wifi/WifiService.java +++ b/services/java/com/android/server/wifi/WifiService.java @@ -170,7 +170,20 @@ public final class WifiService extends IWifiManager.Stub { } /* Client commands are forwarded to state machine */ case WifiManager.CONNECT_NETWORK: - case WifiManager.SAVE_NETWORK: + case WifiManager.SAVE_NETWORK: { + WifiConfiguration config = (WifiConfiguration) msg.obj; + if (config.isValid()) { + mWifiStateMachine.sendMessage(Message.obtain(msg)); + } else { + Slog.d(TAG, "ClientHandler.handleMessage ignoring msg=" + msg); + if (msg.what == WifiManager.CONNECT_NETWORK) { + replyFailed(msg, WifiManager.CONNECT_NETWORK_FAILED); + } else { + replyFailed(msg, WifiManager.SAVE_NETWORK_FAILED); + } + } + break; + } case WifiManager.FORGET_NETWORK: case WifiManager.START_WPS: case WifiManager.CANCEL_WPS: @@ -185,6 +198,17 @@ public final class WifiService extends IWifiManager.Stub { } } } + + private void replyFailed(Message msg, int what) { + Message reply = msg.obtain(); + reply.what = what; + reply.arg1 = WifiManager.INVALID_ARGS; + try { + msg.replyTo.send(reply); + } catch (RemoteException e) { + // There's not much we can do if reply can't be sent! + } + } } private ClientHandler mClientHandler; @@ -555,7 +579,11 @@ public final class WifiService extends IWifiManager.Stub { */ public void setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) { enforceChangePermission(); - mWifiController.obtainMessage(CMD_SET_AP, enabled ? 1 : 0, 0, wifiConfig).sendToTarget(); + if (wifiConfig.isValid()) { + mWifiController.obtainMessage(CMD_SET_AP, enabled ? 1 : 0, 0, wifiConfig).sendToTarget(); + } else { + Slog.e(TAG, "Invalid WifiConfiguration"); + } } /** @@ -588,7 +616,11 @@ public final class WifiService extends IWifiManager.Stub { enforceChangePermission(); if (wifiConfig == null) return; - mWifiStateMachine.setWifiApConfiguration(wifiConfig); + if (wifiConfig.isValid()) { + mWifiStateMachine.setWifiApConfiguration(wifiConfig); + } else { + Slog.e(TAG, "Invalid WifiConfiguration"); + } } /** @@ -646,10 +678,15 @@ public final class WifiService extends IWifiManager.Stub { */ public int addOrUpdateNetwork(WifiConfiguration config) { enforceChangePermission(); - if (mWifiStateMachineChannel != null) { - return mWifiStateMachine.syncAddOrUpdateNetwork(mWifiStateMachineChannel, config); + if (config.isValid()) { + if (mWifiStateMachineChannel != null) { + return mWifiStateMachine.syncAddOrUpdateNetwork(mWifiStateMachineChannel, config); + } else { + Slog.e(TAG, "mWifiStateMachineChannel is not initialized"); + return -1; + } } else { - Slog.e(TAG, "mWifiStateMachineChannel is not initialized"); + Slog.e(TAG, "bad network configuration"); return -1; } } diff --git a/wifi/java/android/net/wifi/WifiConfigStore.java b/wifi/java/android/net/wifi/WifiConfigStore.java index ea7904c..f79a4a6 100644 --- a/wifi/java/android/net/wifi/WifiConfigStore.java +++ b/wifi/java/android/net/wifi/WifiConfigStore.java @@ -115,6 +115,7 @@ class WifiConfigStore { private Context mContext; private static final String TAG = "WifiConfigStore"; private static final boolean DBG = true; + private static final boolean VDBG = false; private static final String SUPPLICANT_CONFIG_FILE = "/data/misc/wifi/wpa_supplicant.conf"; @@ -154,7 +155,7 @@ class WifiConfigStore { private static final String EOS = "eos"; private final LocalLog mLocalLog; - WpaConfigFileObserver mFileObserver; + private final WpaConfigFileObserver mFileObserver; private WifiNative mWifiNative; private final KeyStore mKeyStore = KeyStore.getInstance(); @@ -163,10 +164,13 @@ class WifiConfigStore { mContext = c; mWifiNative = wn; - if (DBG) { + if (VDBG) { mLocalLog = mWifiNative.getLocalLog(); mFileObserver = new WpaConfigFileObserver(); mFileObserver.startWatching(); + } else { + mLocalLog = null; + mFileObserver = null; } } @@ -180,7 +184,7 @@ class WifiConfigStore { public void onEvent(int event, String path) { if (event == CLOSE_WRITE) { File file = new File(SUPPLICANT_CONFIG_FILE); - localLog("wpa_supplicant.conf changed; new size = " + file.length()); + if (VDBG) localLog("wpa_supplicant.conf changed; new size = " + file.length()); } } } @@ -245,7 +249,7 @@ class WifiConfigStore { * @return false if the network id is invalid */ boolean selectNetwork(int netId) { - localLog("selectNetwork", netId); + if (VDBG) localLog("selectNetwork", netId); if (netId == INVALID_NETWORK_ID) return false; // Reset the priority of each network at start or if it goes too high. @@ -282,7 +286,7 @@ class WifiConfigStore { * @return network update result */ NetworkUpdateResult saveNetwork(WifiConfiguration config) { - localLog("saveNetwork", config.networkId); + if (VDBG) localLog("saveNetwork", config.networkId); // A new network cannot have null SSID if (config == null || (config.networkId == INVALID_NETWORK_ID && config.SSID == null)) { @@ -331,7 +335,7 @@ class WifiConfigStore { * @return {@code true} if it succeeds, {@code false} otherwise */ boolean forgetNetwork(int netId) { - localLog("forgetNetwork", netId); + if (VDBG) localLog("forgetNetwork", netId); if (mWifiNative.removeNetwork(netId)) { mWifiNative.saveConfig(); removeConfigAndSendBroadcastIfNeeded(netId); @@ -352,7 +356,7 @@ class WifiConfigStore { * @return network Id */ int addOrUpdateNetwork(WifiConfiguration config) { - localLog("addOrUpdateNetwork", config.networkId); + if (VDBG) localLog("addOrUpdateNetwork", config.networkId); NetworkUpdateResult result = addOrUpdateNetworkNative(config); if (result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) { sendConfiguredNetworksChangedBroadcast(mConfiguredNetworks.get(result.getNetworkId()), @@ -372,7 +376,7 @@ class WifiConfigStore { * @return {@code true} if it succeeds, {@code false} otherwise */ boolean removeNetwork(int netId) { - localLog("removeNetwork", netId); + if (VDBG) localLog("removeNetwork", netId); boolean ret = mWifiNative.removeNetwork(netId); if (ret) { removeConfigAndSendBroadcastIfNeeded(netId); @@ -407,10 +411,10 @@ class WifiConfigStore { boolean enableNetwork(int netId, boolean disableOthers) { boolean ret = enableNetworkWithoutBroadcast(netId, disableOthers); if (disableOthers) { - localLog("enableNetwork(disableOthers=true) ", netId); + if (VDBG) localLog("enableNetwork(disableOthers=true) ", netId); sendConfiguredNetworksChangedBroadcast(); } else { - localLog("enableNetwork(disableOthers=false) ", netId); + if (VDBG) localLog("enableNetwork(disableOthers=false) ", netId); WifiConfiguration enabledNetwork = null; synchronized(mConfiguredNetworks) { enabledNetwork = mConfiguredNetworks.get(netId); @@ -437,7 +441,7 @@ class WifiConfigStore { } void disableAllNetworks() { - localLog("disableAllNetworks"); + if (VDBG) localLog("disableAllNetworks"); boolean networkDisabled = false; for(WifiConfiguration config : mConfiguredNetworks.values()) { if(config != null && config.status != Status.DISABLED) { @@ -470,7 +474,7 @@ class WifiConfigStore { * @return {@code true} if it succeeds, {@code false} otherwise */ boolean disableNetwork(int netId, int reason) { - localLog("disableNetwork", netId); + if (VDBG) localLog("disableNetwork", netId); boolean ret = mWifiNative.disableNetwork(netId); WifiConfiguration network = null; WifiConfiguration config = mConfiguredNetworks.get(netId); @@ -683,33 +687,33 @@ class WifiConfigStore { if (mNetworkIds.containsKey(configKey(config))) { // That SSID is already known, just ignore this duplicate entry - localLog("discarded duplicate network", config.networkId); + if (VDBG) localLog("discarded duplicate network", config.networkId); } else { mConfiguredNetworks.put(config.networkId, config); mNetworkIds.put(configKey(config), config.networkId); - localLog("loaded configured network", config.networkId); + if (VDBG) localLog("loaded configured network", config.networkId); } } readIpAndProxyConfigurations(); sendConfiguredNetworksChangedBroadcast(); - localLog("loadConfiguredNetworks loaded " + mNetworkIds.size() + " networks"); + if (VDBG) localLog("loadConfiguredNetworks loaded " + mNetworkIds.size() + " networks"); if (mNetworkIds.size() == 0) { // no networks? Lets log if the wpa_supplicant.conf file contents BufferedReader reader = null; try { reader = new BufferedReader(new FileReader(SUPPLICANT_CONFIG_FILE)); - localLog("--- Begin wpa_supplicant.conf Contents ---"); + if (VDBG) localLog("--- Begin wpa_supplicant.conf Contents ---"); for (String line = reader.readLine(); line != null; line = reader.readLine()) { - localLog(line); + if (VDBG) localLog(line); } - localLog("--- End wpa_supplicant.conf Contents ---"); + if (VDBG) localLog("--- End wpa_supplicant.conf Contents ---"); } catch (FileNotFoundException e) { - localLog("Could not open " + SUPPLICANT_CONFIG_FILE + ", " + e); + if (VDBG) localLog("Could not open " + SUPPLICANT_CONFIG_FILE + ", " + e); } catch (IOException e) { - localLog("Could not read " + SUPPLICANT_CONFIG_FILE + ", " + e); + if (VDBG) localLog("Could not read " + SUPPLICANT_CONFIG_FILE + ", " + e); } finally { try { if (reader != null) { @@ -1050,7 +1054,7 @@ class WifiConfigStore { * refer to an existing configuration. */ - localLog("addOrUpdateNetworkNative " + config.getPrintableSsid()); + if (VDBG) localLog("addOrUpdateNetworkNative " + config.getPrintableSsid()); int netId = config.networkId; boolean newNetwork = false; diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java index de377ee..2ce584b 100644 --- a/wifi/java/android/net/wifi/WifiConfiguration.java +++ b/wifi/java/android/net/wifi/WifiConfiguration.java @@ -348,6 +348,20 @@ public class WifiConfiguration implements Parcelable { linkProperties = new LinkProperties(); } + /** + * indicates whether the configuration is valid + * @return true if valid, false otherwise + * @hide + */ + public boolean isValid() { + if (allowedKeyManagement.cardinality() > 1) { + return false; + } + + // TODO: Add more checks + return true; + } + @Override public String toString() { StringBuilder sbuf = new StringBuilder(); diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index c3bf9c1..3223cb3 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -1370,6 +1370,13 @@ public class WifiManager { /** WPS timed out {@hide} */ public static final int WPS_TIMED_OUT = 7; + /** + * Passed with {@link ActionListener#onFailure}. + * Indicates that the operation failed due to invalid inputs + * @hide + */ + public static final int INVALID_ARGS = 8; + /** Interface for callback invocation on an application action {@hide} */ public interface ActionListener { /** The operation succeeded */ diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java index 3deac1b..520668e 100644 --- a/wifi/java/android/net/wifi/WifiNative.java +++ b/wifi/java/android/net/wifi/WifiNative.java @@ -39,6 +39,7 @@ import java.util.Locale; public class WifiNative { private static final boolean DBG = false; + private static final boolean VDBG = false; private final String mTAG; private static final int DEFAULT_GROUP_OWNER_INTENT = 6; @@ -117,12 +118,12 @@ public class WifiNative { public boolean connectToSupplicant() { // No synchronization necessary .. it is implemented in WifiMonitor - localLog(mInterfacePrefix + "connectToSupplicant"); + if (VDBG) localLog(mInterfacePrefix + "connectToSupplicant"); return connectToSupplicantNative(); } public void closeSupplicantConnection() { - localLog(mInterfacePrefix + "closeSupplicantConnection"); + if (VDBG) localLog(mInterfacePrefix + "closeSupplicantConnection"); closeSupplicantConnectionNative(); } @@ -135,9 +136,9 @@ public class WifiNative { if (DBG) Log.d(mTAG, "doBoolean: " + command); synchronized (mLock) { int cmdId = getNewCmdIdLocked(); - localLog(cmdId + "->" + mInterfacePrefix + command); + if (VDBG) localLog(cmdId + "->" + mInterfacePrefix + command); boolean result = doBooleanCommandNative(mInterfacePrefix + command); - localLog(cmdId + "<-" + result); + if (VDBG) localLog(cmdId + "<-" + result); if (DBG) Log.d(mTAG, " returned " + result); return result; } @@ -147,9 +148,9 @@ public class WifiNative { if (DBG) Log.d(mTAG, "doInt: " + command); synchronized (mLock) { int cmdId = getNewCmdIdLocked(); - localLog(cmdId + "->" + mInterfacePrefix + command); + if (VDBG) localLog(cmdId + "->" + mInterfacePrefix + command); int result = doIntCommandNative(mInterfacePrefix + command); - localLog(cmdId + "<-" + result); + if (VDBG) localLog(cmdId + "<-" + result); if (DBG) Log.d(mTAG, " returned " + result); return result; } @@ -159,9 +160,9 @@ public class WifiNative { if (DBG) Log.d(mTAG, "doString: " + command); synchronized (mLock) { int cmdId = getNewCmdIdLocked(); - localLog(cmdId + "->" + mInterfacePrefix + command); + if (VDBG) localLog(cmdId + "->" + mInterfacePrefix + command); String result = doStringCommandNative(mInterfacePrefix + command); - localLog(cmdId + "<-" + result); + if (VDBG) localLog(cmdId + "<-" + result); if (DBG) Log.d(mTAG, " returned " + result); return result; } |