diff options
29 files changed, 1340 insertions, 50 deletions
@@ -53,6 +53,16 @@ Media Codecs These files are Copyright 1998 - 2009 PacketVideo, but released under the Apache2 License. + ========================================================================= + == NOTICE file corresponding to the section 4 d of == + == the Apache License, Version 2.0, == + == in this case for Additional Codecs code. == + ========================================================================= + +Additional Codecs +These files are Copyright 2003-2010 VisualOn, but released under +the Apache2 License. + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ diff --git a/api/current.xml b/api/current.xml index 05658e7..48816af 100644 --- a/api/current.xml +++ b/api/current.xml @@ -45952,6 +45952,19 @@ <exception name="PackageManager.NameNotFoundException" type="android.content.pm.PackageManager.NameNotFoundException"> </exception> </method> +<method name="getPackageObbPaths" + return="java.lang.String[]" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="packageName" type="java.lang.String"> +</parameter> +</method> <method name="getPackagesForUid" return="java.lang.String[]" abstract="true" @@ -46423,6 +46436,21 @@ <parameter name="flags" type="int"> </parameter> </method> +<method name="setPackageObbPaths" + return="void" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="packageName" type="java.lang.String"> +</parameter> +<parameter name="paths" type="java.lang.String[]"> +</parameter> +</method> <field name="COMPONENT_ENABLED_STATE_DEFAULT" type="int" transient="false" @@ -59862,6 +59890,16 @@ visibility="public" > </field> +<field name="inPreferQualityOverSpeed" + type="boolean" + transient="false" + volatile="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="inPreferredConfig" type="android.graphics.Bitmap.Config" transient="false" @@ -59974,6 +60012,146 @@ > </field> </class> +<class name="BitmapRegionDecoder" + extends="java.lang.Object" + abstract="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +<method name="decodeRegion" + return="android.graphics.Bitmap" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="rect" type="android.graphics.Rect"> +</parameter> +<parameter name="options" type="android.graphics.BitmapFactory.Options"> +</parameter> +</method> +<method name="getHeight" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getWidth" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="isRecycled" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="newInstance" + return="android.graphics.BitmapRegionDecoder" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="data" type="byte[]"> +</parameter> +<parameter name="offset" type="int"> +</parameter> +<parameter name="length" type="int"> +</parameter> +<parameter name="isShareable" type="boolean"> +</parameter> +<exception name="IOException" type="java.io.IOException"> +</exception> +</method> +<method name="newInstance" + return="android.graphics.BitmapRegionDecoder" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="fd" type="java.io.FileDescriptor"> +</parameter> +<parameter name="isShareable" type="boolean"> +</parameter> +<exception name="IOException" type="java.io.IOException"> +</exception> +</method> +<method name="newInstance" + return="android.graphics.BitmapRegionDecoder" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="is" type="java.io.InputStream"> +</parameter> +<parameter name="isShareable" type="boolean"> +</parameter> +<exception name="IOException" type="java.io.IOException"> +</exception> +</method> +<method name="newInstance" + return="android.graphics.BitmapRegionDecoder" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="pathName" type="java.lang.String"> +</parameter> +<parameter name="isShareable" type="boolean"> +</parameter> +<exception name="IOException" type="java.io.IOException"> +</exception> +</method> +<method name="recycle" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +</class> <class name="BitmapShader" extends="android.graphics.Shader" abstract="false" @@ -100228,6 +100406,36 @@ deprecated="not deprecated" visibility="public" > +<method name="disableForegroundDispatch" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="activity" type="android.app.Activity"> +</parameter> +</method> +<method name="enableForegroundDispatch" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="activity" type="android.app.Activity"> +</parameter> +<parameter name="intent" type="android.app.PendingIntent"> +</parameter> +<parameter name="filters" type="android.content.IntentFilter..."> +</parameter> +</method> <method name="getDefaultAdapter" return="android.nfc.NfcAdapter" abstract="false" @@ -159105,6 +159313,19 @@ <exception name="PackageManager.NameNotFoundException" type="android.content.pm.PackageManager.NameNotFoundException"> </exception> </method> +<method name="getPackageObbPaths" + return="java.lang.String[]" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="packageName" type="java.lang.String"> +</parameter> +</method> <method name="getPackagesForUid" return="java.lang.String[]" abstract="false" @@ -159589,6 +159810,21 @@ <parameter name="path" type="java.lang.String"> </parameter> </method> +<method name="setPackageObbPaths" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="packageName" type="java.lang.String"> +</parameter> +<parameter name="paths" type="java.lang.String[]"> +</parameter> +</method> </class> <class name="MockResources" extends="android.content.res.Resources" @@ -226180,7 +226416,7 @@ deprecated="not deprecated" visibility="public" > -<parameter name="arg0" type="T"> +<parameter name="t" type="T"> </parameter> </method> </interface> diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index f25c4c3..a54b305 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -632,7 +632,7 @@ public class Activity extends ContextThemeWrapper /*package*/ HashMap<String,Object> mLastNonConfigurationChildInstances; Activity mParent; boolean mCalled; - private boolean mResumed; + /*package*/ boolean mResumed; private boolean mStopped; boolean mFinished; boolean mStartedActivity; @@ -3827,9 +3827,8 @@ public class Activity extends ContextThemeWrapper mLastNonConfigurationInstance = null; - // First call onResume() -before- setting mResumed, so we don't - // send out any status bar / menu notifications the client makes. mCalled = false; + // mResumed is set by the instrumentation mInstrumentation.callActivityOnResume(this); if (!mCalled) { throw new SuperNotCalledException( @@ -3838,7 +3837,6 @@ public class Activity extends ContextThemeWrapper } // Now really resume, and install the current status bar and menu. - mResumed = true; mCalled = false; onPostResume(); if (!mCalled) { @@ -3857,6 +3855,7 @@ public class Activity extends ContextThemeWrapper "Activity " + mComponent.toShortString() + " did not call through to super.onPause()"); } + mResumed = false; } final void performUserLeaving() { @@ -3891,10 +3890,12 @@ public class Activity extends ContextThemeWrapper mStopped = true; } - mResumed = false; } - final boolean isResumed() { + /** + * @hide + */ + public final boolean isResumed() { return mResumed; } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 78df780..e3fa32c 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -185,6 +185,9 @@ public final class ActivityThread { final HashMap<IBinder, ProviderClientRecord> mLocalProviders = new HashMap<IBinder, ProviderClientRecord>(); + final HashMap<Activity, ArrayList<OnActivityPausedListener>> mOnPauseListeners + = new HashMap<Activity, ArrayList<OnActivityPausedListener>>(); + final GcIdler mGcIdler = new GcIdler(); boolean mGcIdlerScheduled = false; @@ -1424,6 +1427,18 @@ public final class ActivityThread { } } + public void registerOnActivityPausedListener(Activity activity, + OnActivityPausedListener listener) { + synchronized (mOnPauseListeners) { + ArrayList<OnActivityPausedListener> list = mOnPauseListeners.get(activity); + if (list == null) { + list = new ArrayList<OnActivityPausedListener>(); + mOnPauseListeners.put(activity, list); + } + list.add(listener); + } + } + public final ActivityInfo resolveActivityInfo(Intent intent) { ActivityInfo aInfo = intent.resolveActivityInfo( mInitialApplication.getPackageManager(), PackageManager.GET_SHARED_LIBRARY_FILES); @@ -2333,6 +2348,17 @@ public final class ActivityThread { } } r.paused = true; + + // Notify any outstanding on paused listeners + ArrayList<OnActivityPausedListener> listeners; + synchronized (mOnPauseListeners) { + listeners = mOnPauseListeners.remove(r.activity); + } + int size = (listeners != null ? listeners.size() : 0); + for (int i = 0; i < size; i++) { + listeners.get(i).onPaused(r.activity); + } + return state; } diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 7e7cd7a..18ab478 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -2700,14 +2700,24 @@ class ContextImpl extends Context { } @Override - public void setPackageObbPath(String packageName, String path) { + public void setPackageObbPaths(String packageName, String[] paths) { try { - mPM.setPackageObbPath(packageName, path); + mPM.setPackageObbPaths(packageName, paths); } catch (RemoteException e) { // Should never happen! } } + @Override + public String[] getPackageObbPaths(String packageName) { + try { + return mPM.getPackageObbPaths(packageName); + } catch (RemoteException e) { + // Should never happen! + } + return null; + } + private final ContextImpl mContext; private final IPackageManager mPM; diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index b8c3aa3..5c333a2 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -1146,6 +1146,7 @@ public class Instrumentation { * @param activity The activity being resumed. */ public void callActivityOnResume(Activity activity) { + activity.mResumed = true; activity.onResume(); if (mActivityMonitors != null) { diff --git a/core/java/android/app/OnActivityPausedListener.java b/core/java/android/app/OnActivityPausedListener.java new file mode 100644 index 0000000..379f133 --- /dev/null +++ b/core/java/android/app/OnActivityPausedListener.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.app; + +/** + * A listener that is called when an Activity is paused. Since this is tracked client side + * it should not be trusted to represent the exact current state, but can be used as a hint + * for cleanup. + * + * @hide + */ +public interface OnActivityPausedListener { + /** + * Called when the given activity is paused. + */ + public void onPaused(Activity activity); +} diff --git a/core/java/android/bluetooth/BluetoothDeviceProfileState.java b/core/java/android/bluetooth/BluetoothDeviceProfileState.java index 9be4c8f..954fde5 100644 --- a/core/java/android/bluetooth/BluetoothDeviceProfileState.java +++ b/core/java/android/bluetooth/BluetoothDeviceProfileState.java @@ -129,10 +129,6 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine newState == BluetoothA2dp.STATE_DISCONNECTED) { sendMessage(TRANSITION_TO_STABLE); } - } else if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) { - Message msg = new Message(); - msg.what = AUTO_CONNECT_PROFILES; - sendMessageDelayed(msg, AUTO_CONNECT_DELAY); } else if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) { // This is technically not needed, but we can get stuck sometimes. // For example, if incoming A2DP fails, we are not informed by Bluez diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 4cff3bb..44b0c96 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -322,5 +322,6 @@ interface IPackageManager { boolean setInstallLocation(int loc); int getInstallLocation(); - void setPackageObbPath(String packageName, String path); + void setPackageObbPaths(in String packageName, in String[] paths); + String[] getPackageObbPaths(in String packageName); } diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index b5d1653..a1c29f7 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -2273,15 +2273,30 @@ public abstract class PackageManager { String packageName, IPackageMoveObserver observer, int flags); /** - * Sets the Opaque Binary Blob (OBB) file location. + * Sets the Opaque Binary Blob (OBB) file path associated with a package + * name. The caller must have the + * {@link android.Manifest.permission#INSTALL_PACKAGES} permission. * <p> * NOTE: The existence or format of this file is not currently checked, but * it may be in the future. * * @param packageName Name of the package with which to associate the .obb - * file - * @param path Path on the filesystem to the .obb file - * @hide + * file. + * @param paths Arrays of paths on the filesystem to the .obb files + * associated with the package. + * @see #getPackageObbPaths(String) + */ + public abstract void setPackageObbPaths(String packageName, String[] paths); + + /** + * Gets the Opaque Binary Blob (OBB) file path associated with the package. + * The caller must be the owner of the package queried or have the + * {@link android.Manifest.permission#INSTALL_PACKAGES} permission. + * + * @param packageName Name of the package with which to associate the .obb + * file. + * @return array of paths to .obb files associated with the package + * @see #setPackageObbPaths(String, String[]) */ - public abstract void setPackageObbPath(String packageName, String path); + public abstract String[] getPackageObbPaths(String packageName); } diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 80b8d92..16805b2 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -2601,14 +2601,8 @@ public class PackageParser { int priority = sa.getInt( com.android.internal.R.styleable.AndroidManifestIntentFilter_priority, 0); - if (priority > 0 && isActivity && (flags&PARSE_IS_SYSTEM) == 0) { - Log.w(TAG, "Activity with priority > 0, forcing to 0 at " - + mArchiveSourcePath + " " - + parser.getPositionDescription()); - priority = 0; - } outInfo.setPriority(priority); - + TypedValue v = sa.peekValue( com.android.internal.R.styleable.AndroidManifestIntentFilter_label); if (v != null && (outInfo.labelRes=v.resourceId) == 0) { diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl index a663fb8..cb9fc9d 100644 --- a/core/java/android/nfc/INfcAdapter.aidl +++ b/core/java/android/nfc/INfcAdapter.aidl @@ -16,6 +16,9 @@ package android.nfc; +import android.app.PendingIntent; +import android.content.ComponentName; +import android.content.IntentFilter; import android.nfc.NdefMessage; import android.nfc.Tag; import android.nfc.ILlcpSocket; @@ -44,6 +47,9 @@ interface INfcAdapter NdefMessage localGet(); void localSet(in NdefMessage message); void openTagConnection(in Tag tag); + void enableForegroundDispatch(in ComponentName activity, in PendingIntent intent, + in IntentFilter[] filters); + void disableForegroundDispatch(in ComponentName activity); // Non-public methods // TODO: check and complete diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java index cfbe581..c9d6af8 100644 --- a/core/java/android/nfc/NfcAdapter.java +++ b/core/java/android/nfc/NfcAdapter.java @@ -18,8 +18,12 @@ package android.nfc; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.app.Activity; import android.app.ActivityThread; +import android.app.OnActivityPausedListener; +import android.app.PendingIntent; import android.content.Context; +import android.content.IntentFilter; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.nfc.technology.TagTechnology; @@ -100,6 +104,20 @@ public final class NfcAdapter { "android.nfc.action.TRANSACTION_DETECTED"; /** + * Broadcast Action: an RF field ON has been detected. + * @hide + */ + public static final String ACTION_RF_FIELD_ON_DETECTED = + "android.nfc.action.RF_FIELD_ON_DETECTED"; + + /** + * Broadcast Action: an RF Field OFF has been detected. + * @hide + */ + public static final String ACTION_RF_FIELD_OFF_DETECTED = + "android.nfc.action.RF_FIELD_OFF_DETECTED"; + + /** * Broadcast Action: an adapter's state changed between enabled and disabled. * * The new value is stored in the extra EXTRA_NEW_BOOLEAN_STATE and just contains @@ -197,8 +215,6 @@ public final class NfcAdapter { private static INfcAdapter sService; private static INfcTag sTagService; - private final Context mContext; - /** * Helper to check if this device has FEATURE_NFC, but without using * a context. @@ -294,7 +310,6 @@ public final class NfcAdapter { if (setupService() == null) { throw new UnsupportedOperationException(); } - mContext = context; } /** @@ -396,6 +411,66 @@ public final class NfcAdapter { } } + class ForegroundDispatchPausedListener implements OnActivityPausedListener { + @Override + public void onPaused(Activity activity) { + disableForegroundDispatchInternal(activity, true); + } + } + + /** + * Enables foreground dispatching to the given Activity. This will force all NFC Intents that + * match the given filters to be delivered to the activity bypassing the standard dispatch + * mechanism. + * + * This method must be called from the main thread. + * + * @param activity the Activity to dispatch to + * @param intent the PendingIntent to start for the dispatch + * @param filters the IntentFilters to override dispatching for + * @throws IllegalStateException + */ + public void enableForegroundDispatch(Activity activity, PendingIntent intent, + IntentFilter... filters) { + if (activity == null || intent == null || filters == null) { + throw new NullPointerException(); + } + if (!activity.isResumed()) { + throw new IllegalStateException("Foregorund dispatching can onlly be enabled " + + "when your activity is resumed"); + } + try { + ActivityThread.currentActivityThread().registerOnActivityPausedListener(activity, + new ForegroundDispatchPausedListener()); + sService.enableForegroundDispatch(activity.getComponentName(), intent, filters); + } catch (RemoteException e) { + attemptDeadServiceRecovery(e); + } + } + + /** + * Disables foreground activity dispatching setup with + * {@link #enableForegroundDispatch}. This must be called before the Activity returns from + * it's <code>onPause()</code> or this method will throw an IllegalStateException. + * + * This method must be called from the main thread. + */ + public void disableForegroundDispatch(Activity activity) { + disableForegroundDispatchInternal(activity, false); + } + + void disableForegroundDispatchInternal(Activity activity, boolean force) { + try { + sService.disableForegroundDispatch(activity.getComponentName()); + if (!force && !activity.isResumed()) { + throw new IllegalStateException("You must disable forgeground dispatching " + + "while your activity is still resumed"); + } + } catch (RemoteException e) { + attemptDeadServiceRecovery(e); + } + } + /** * Retrieve a TagTechnology object used to interact with a Tag that is * in field. diff --git a/core/jni/Android.mk b/core/jni/Android.mk index c3f393d..12b9ca5 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -103,6 +103,7 @@ LOCAL_SRC_FILES:= \ android_graphics_PixelFormat.cpp \ android/graphics/Picture.cpp \ android/graphics/PorterDuff.cpp \ + android/graphics/BitmapRegionDecoder.cpp \ android/graphics/Rasterizer.cpp \ android/graphics/Region.cpp \ android/graphics/Shader.cpp \ diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 648d93f..d9759bf 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -53,6 +53,7 @@ extern int register_android_os_Binder(JNIEnv* env); extern int register_android_os_Process(JNIEnv* env); extern int register_android_graphics_Bitmap(JNIEnv*); extern int register_android_graphics_BitmapFactory(JNIEnv*); +extern int register_android_graphics_BitmapRegionDecoder(JNIEnv*); extern int register_android_graphics_Camera(JNIEnv* env); extern int register_android_graphics_Graphics(JNIEnv* env); extern int register_android_graphics_Interpolator(JNIEnv* env); @@ -1209,6 +1210,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_graphics_Bitmap), REG_JNI(register_android_graphics_BitmapFactory), + REG_JNI(register_android_graphics_BitmapRegionDecoder), REG_JNI(register_android_graphics_Camera), REG_JNI(register_android_graphics_Canvas), REG_JNI(register_android_graphics_ColorFilter), diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp index 90a0243..987dd4f 100644 --- a/core/jni/android/graphics/BitmapFactory.cpp +++ b/core/jni/android/graphics/BitmapFactory.cpp @@ -27,6 +27,7 @@ jfieldID gOptions_ditherFieldID; jfieldID gOptions_purgeableFieldID; jfieldID gOptions_shareableFieldID; jfieldID gOptions_nativeAllocFieldID; +jfieldID gOptions_preferQualityOverSpeedFieldID; jfieldID gOptions_widthFieldID; jfieldID gOptions_heightFieldID; jfieldID gOptions_mimeFieldID; @@ -180,6 +181,7 @@ static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding, bool isPurgeable = forcePurgeable || (allowPurgeable && optionsPurgeable(env, options)); bool reportSizeToVM = optionsReportSizeToVM(env, options); + bool preferQualityOverSpeed = false; if (NULL != options) { sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID); @@ -194,6 +196,8 @@ static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding, jobject jconfig = env->GetObjectField(options, gOptions_configFieldID); prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig); doDither = env->GetBooleanField(options, gOptions_ditherFieldID); + preferQualityOverSpeed = env->GetBooleanField(options, + gOptions_preferQualityOverSpeedFieldID); } SkImageDecoder* decoder = SkImageDecoder::Factory(stream); @@ -203,6 +207,7 @@ static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding, decoder->setSampleSize(sampleSize); decoder->setDitherImage(doDither); + decoder->setPreferQualityOverSpeed(preferQualityOverSpeed); NinePatchPeeker peeker(decoder); JavaPixelAllocator javaAllocator(env, reportSizeToVM); @@ -551,6 +556,8 @@ int register_android_graphics_BitmapFactory(JNIEnv* env) { gOptions_purgeableFieldID = getFieldIDCheck(env, gOptions_class, "inPurgeable", "Z"); gOptions_shareableFieldID = getFieldIDCheck(env, gOptions_class, "inInputShareable", "Z"); gOptions_nativeAllocFieldID = getFieldIDCheck(env, gOptions_class, "inNativeAlloc", "Z"); + gOptions_preferQualityOverSpeedFieldID = getFieldIDCheck(env, gOptions_class, + "inPreferQualityOverSpeed", "Z"); gOptions_widthFieldID = getFieldIDCheck(env, gOptions_class, "outWidth", "I"); gOptions_heightFieldID = getFieldIDCheck(env, gOptions_class, "outHeight", "I"); gOptions_mimeFieldID = getFieldIDCheck(env, gOptions_class, "outMimeType", "Ljava/lang/String;"); diff --git a/core/jni/android/graphics/BitmapFactory.h b/core/jni/android/graphics/BitmapFactory.h index f868434..9ae61bc 100644 --- a/core/jni/android/graphics/BitmapFactory.h +++ b/core/jni/android/graphics/BitmapFactory.h @@ -11,6 +11,7 @@ extern jfieldID gOptions_ditherFieldID; extern jfieldID gOptions_purgeableFieldID; extern jfieldID gOptions_shareableFieldID; extern jfieldID gOptions_nativeAllocFieldID; +extern jfieldID gOptions_preferQualityOverSpeedFieldID; extern jfieldID gOptions_widthFieldID; extern jfieldID gOptions_heightFieldID; extern jfieldID gOptions_mimeFieldID; diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp new file mode 100644 index 0000000..91a8202 --- /dev/null +++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp @@ -0,0 +1,326 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "BitmapRegionDecoder" + +#include "SkBitmap.h" +#include "SkImageEncoder.h" +#include "GraphicsJNI.h" +#include "SkUtils.h" +#include "SkTemplates.h" +#include "SkPixelRef.h" +#include "SkStream.h" +#include "BitmapFactory.h" +#include "AutoDecodeCancel.h" +#include "SkBitmapRegionDecoder.h" +#include "CreateJavaOutputStreamAdaptor.h" +#include "Utils.h" + +#include <android_runtime/AndroidRuntime.h> +#include "android_util_Binder.h" +#include "android_nio_utils.h" +#include "CreateJavaOutputStreamAdaptor.h" + +#include <binder/Parcel.h> +#include <jni.h> +#include <utils/Asset.h> +#include <sys/stat.h> + +static jclass gFileDescriptor_class; +static jfieldID gFileDescriptor_descriptor; + +#if 0 + #define TRACE_BITMAP(code) code +#else + #define TRACE_BITMAP(code) +#endif + +using namespace android; + +static SkMemoryStream* buildSkMemoryStream(SkStream *stream) { + size_t bufferSize = 4096; + size_t streamLen = 0; + size_t len; + char* data = (char*)sk_malloc_throw(bufferSize); + + while ((len = stream->read(data + streamLen, + bufferSize - streamLen)) != 0) { + streamLen += len; + if (streamLen == bufferSize) { + bufferSize *= 2; + data = (char*)sk_realloc_throw(data, bufferSize); + } + } + data = (char*)sk_realloc_throw(data, streamLen); + + SkMemoryStream* streamMem = new SkMemoryStream(); + streamMem->setMemoryOwned(data, streamLen); + return streamMem; +} + +static jobject doBuildTileIndex(JNIEnv* env, SkStream* stream) { + SkImageDecoder* decoder = SkImageDecoder::Factory(stream); + int width, height; + if (NULL == decoder) { + doThrowIOE(env, "Image format not supported"); + return nullObjectReturn("SkImageDecoder::Factory returned null"); + } + + JavaPixelAllocator *javaAllocator = new JavaPixelAllocator(env, true); + decoder->setAllocator(javaAllocator); + JavaMemoryUsageReporter *javaMemoryReporter = new JavaMemoryUsageReporter(env); + decoder->setReporter(javaMemoryReporter); + javaAllocator->unref(); + javaMemoryReporter->unref(); + + if (!decoder->buildTileIndex(stream, &width, &height)) { + char msg[100]; + snprintf(msg, sizeof(msg), "Image failed to decode using %s decoder", + decoder->getFormatName()); + doThrowIOE(env, msg); + return nullObjectReturn("decoder->buildTileIndex returned false"); + } + + SkBitmapRegionDecoder *bm = new SkBitmapRegionDecoder(decoder, width, height); + + return GraphicsJNI::createBitmapRegionDecoder(env, bm); +} + +static jobject nativeNewInstanceFromByteArray(JNIEnv* env, jobject, jbyteArray byteArray, + int offset, int length, jboolean isShareable) { + /* If isShareable we could decide to just wrap the java array and + share it, but that means adding a globalref to the java array object + For now we just always copy the array's data if isShareable. + */ + AutoJavaByteArray ar(env, byteArray); + SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length, true); + return doBuildTileIndex(env, stream); +} + +static jobject nativeNewInstanceFromFileDescriptor(JNIEnv* env, jobject clazz, + jobject fileDescriptor, jboolean isShareable) { + NPE_CHECK_RETURN_ZERO(env, fileDescriptor); + + jint descriptor = env->GetIntField(fileDescriptor, + gFileDescriptor_descriptor); + SkStream *stream = NULL; + struct stat fdStat; + int newFD; + if (fstat(descriptor, &fdStat) == -1) { + doThrowIOE(env, "broken file descriptor"); + return nullObjectReturn("fstat return -1"); + } + + if (isShareable && + S_ISREG(fdStat.st_mode) && + (newFD = ::dup(descriptor)) != -1) { + SkFDStream* fdStream = new SkFDStream(newFD, true); + if (!fdStream->isValid()) { + fdStream->unref(); + return NULL; + } + stream = fdStream; + } else { + /* Restore our offset when we leave, so we can be called more than once + with the same descriptor. This is only required if we didn't dup the + file descriptor, but it is OK to do it all the time. + */ + AutoFDSeek as(descriptor); + + SkFDStream* fdStream = new SkFDStream(descriptor, false); + if (!fdStream->isValid()) { + fdStream->unref(); + return NULL; + } + stream = buildSkMemoryStream(fdStream); + fdStream->unref(); + } + + return doBuildTileIndex(env, stream); +} + +static jobject nativeNewInstanceFromStream(JNIEnv* env, jobject clazz, + jobject is, // InputStream + jbyteArray storage, // byte[] + jboolean isShareable) { + jobject largeBitmap = NULL; + SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage, 1024); + + if (stream) { + // for now we don't allow shareable with java inputstreams + SkMemoryStream *mStream = buildSkMemoryStream(stream); + largeBitmap = doBuildTileIndex(env, mStream); + stream->unref(); + } + return largeBitmap; +} + +static jobject nativeNewInstanceFromAsset(JNIEnv* env, jobject clazz, + jint native_asset, // Asset + jboolean isShareable) { + SkStream* stream, *assStream; + Asset* asset = reinterpret_cast<Asset*>(native_asset); + assStream = new AssetStreamAdaptor(asset); + stream = buildSkMemoryStream(assStream); + assStream->unref(); + return doBuildTileIndex(env, stream); +} + +/* + * nine patch not supported + * + * purgeable not supported + * reportSizeToVM not supported + */ +static jobject nativeDecodeRegion(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd, + int start_x, int start_y, int width, int height, jobject options) { + SkImageDecoder *decoder = brd->getDecoder(); + int sampleSize = 1; + SkBitmap::Config prefConfig = SkBitmap::kNo_Config; + bool doDither = true; + bool preferQualityOverSpeed = false; + + if (NULL != options) { + sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID); + // initialize these, in case we fail later on + env->SetIntField(options, gOptions_widthFieldID, -1); + env->SetIntField(options, gOptions_heightFieldID, -1); + env->SetObjectField(options, gOptions_mimeFieldID, 0); + + jobject jconfig = env->GetObjectField(options, gOptions_configFieldID); + prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig); + doDither = env->GetBooleanField(options, gOptions_ditherFieldID); + preferQualityOverSpeed = env->GetBooleanField(options, + gOptions_preferQualityOverSpeedFieldID); + } + + decoder->setDitherImage(doDither); + decoder->setPreferQualityOverSpeed(preferQualityOverSpeed); + SkBitmap* bitmap = new SkBitmap; + SkAutoTDelete<SkBitmap> adb(bitmap); + AutoDecoderCancel adc(options, decoder); + + // To fix the race condition in case "requestCancelDecode" + // happens earlier than AutoDecoderCancel object is added + // to the gAutoDecoderCancelMutex linked list. + if (NULL != options && env->GetBooleanField(options, gOptions_mCancelID)) { + return nullObjectReturn("gOptions_mCancelID");; + } + + SkIRect region; + region.fLeft = start_x; + region.fTop = start_y; + region.fRight = start_x + width; + region.fBottom = start_y + height; + + if (!brd->decodeRegion(bitmap, region, prefConfig, sampleSize)) { + return nullObjectReturn("decoder->decodeRegion returned false"); + } + + // update options (if any) + if (NULL != options) { + env->SetIntField(options, gOptions_widthFieldID, bitmap->width()); + env->SetIntField(options, gOptions_heightFieldID, bitmap->height()); + // TODO: set the mimeType field with the data from the codec. + // but how to reuse a set of strings, rather than allocating new one + // each time? + env->SetObjectField(options, gOptions_mimeFieldID, + getMimeTypeString(env, decoder->getFormat())); + } + + // detach bitmap from its autotdeleter, since we want to own it now + adb.detach(); + + SkPixelRef* pr; + pr = bitmap->pixelRef(); + // promise we will never change our pixels (great for sharing and pictures) + pr->setImmutable(); + // now create the java bitmap + return GraphicsJNI::createBitmap(env, bitmap, false, NULL); +} + +static int nativeGetHeight(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd) { + return brd->getHeight(); +} + +static int nativeGetWidth(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd) { + return brd->getWidth(); +} + +static void nativeClean(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd) { + delete brd; +} + +/////////////////////////////////////////////////////////////////////////////// + +#include <android_runtime/AndroidRuntime.h> + +static JNINativeMethod gBitmapRegionDecoderMethods[] = { + { "nativeDecodeRegion", + "(IIIIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", + (void*)nativeDecodeRegion}, + + { "nativeGetHeight", "(I)I", (void*)nativeGetHeight}, + + { "nativeGetWidth", "(I)I", (void*)nativeGetWidth}, + + { "nativeClean", "(I)V", (void*)nativeClean}, + + { "nativeNewInstance", + "([BIIZ)Landroid/graphics/BitmapRegionDecoder;", + (void*)nativeNewInstanceFromByteArray + }, + + { "nativeNewInstance", + "(Ljava/io/InputStream;[BZ)Landroid/graphics/BitmapRegionDecoder;", + (void*)nativeNewInstanceFromStream + }, + + { "nativeNewInstance", + "(Ljava/io/FileDescriptor;Z)Landroid/graphics/BitmapRegionDecoder;", + (void*)nativeNewInstanceFromFileDescriptor + }, + + { "nativeNewInstance", + "(IZ)Landroid/graphics/BitmapRegionDecoder;", + (void*)nativeNewInstanceFromAsset + }, +}; + +#define kClassPathName "android/graphics/BitmapRegionDecoder" + +static jclass make_globalref(JNIEnv* env, const char classname[]) { + jclass c = env->FindClass(classname); + SkASSERT(c); + return (jclass)env->NewGlobalRef(c); +} + +static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz, + const char fieldname[], const char type[]) { + jfieldID id = env->GetFieldID(clazz, fieldname, type); + SkASSERT(id); + return id; +} + +int register_android_graphics_BitmapRegionDecoder(JNIEnv* env); +int register_android_graphics_BitmapRegionDecoder(JNIEnv* env) +{ + + gFileDescriptor_class = make_globalref(env, "java/io/FileDescriptor"); + gFileDescriptor_descriptor = getFieldIDCheck(env, gFileDescriptor_class, "descriptor", "I"); + return android::AndroidRuntime::registerNativeMethods(env, kClassPathName, + gBitmapRegionDecoderMethods, SK_ARRAY_COUNT(gBitmapRegionDecoderMethods)); +} diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp index 195f4d2..9467be8 100644 --- a/core/jni/android/graphics/Graphics.cpp +++ b/core/jni/android/graphics/Graphics.cpp @@ -46,7 +46,7 @@ void doThrowOOME(JNIEnv* env, const char* msg) { } void doThrowIOE(JNIEnv* env, const char* msg) { - doThrow(env, "java/lang/IOException", msg); + doThrow(env, "java/io/IOException", msg); } bool GraphicsJNI::hasException(JNIEnv *env) { @@ -168,6 +168,9 @@ static jmethodID gBitmap_allocBufferMethodID; static jclass gBitmapConfig_class; static jfieldID gBitmapConfig_nativeInstanceID; +static jclass gBitmapRegionDecoder_class; +static jmethodID gBitmapRegionDecoder_constructorMethodID; + static jclass gCanvas_class; static jfieldID gCanvas_nativeInstanceID; @@ -374,6 +377,24 @@ jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, bool isMutable, return obj; } +jobject GraphicsJNI::createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap) +{ + SkASSERT(bitmap != NULL); + + jobject obj = env->AllocObject(gBitmapRegionDecoder_class); + if (hasException(env)) { + obj = NULL; + return obj; + } + if (obj) { + env->CallVoidMethod(obj, gBitmapRegionDecoder_constructorMethodID, (jint)bitmap); + if (hasException(env)) { + obj = NULL; + } + } + return obj; +} + jobject GraphicsJNI::createRegion(JNIEnv* env, SkRegion* region) { SkASSERT(region != NULL); @@ -592,6 +613,9 @@ int register_android_graphics_Graphics(JNIEnv* env) gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "<init>", "(IZ[BI)V"); + gBitmapRegionDecoder_class = make_globalref(env, "android/graphics/BitmapRegionDecoder"); + gBitmapRegionDecoder_constructorMethodID = env->GetMethodID(gBitmapRegionDecoder_class, "<init>", "(I)V"); + gBitmapConfig_class = make_globalref(env, "android/graphics/Bitmap$Config"); gBitmapConfig_nativeInstanceID = getFieldIDCheck(env, gBitmapConfig_class, "nativeInt", "I"); diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h index 1f94418..d0f9125 100644 --- a/core/jni/android/graphics/GraphicsJNI.h +++ b/core/jni/android/graphics/GraphicsJNI.h @@ -4,6 +4,7 @@ #include "SkPoint.h" #include "SkRect.h" #include "SkBitmap.h" +#include "../images/SkBitmapRegionDecoder.h" #include "../images/SkImageDecoder.h" #include <jni.h> @@ -55,6 +56,8 @@ public: static jobject createRegion(JNIEnv* env, SkRegion* region); + static jobject createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap); + /** Set a pixelref for the bitmap (needs setConfig to already be called) Returns true on success. If it returns false, then it failed, and the appropriate exception will have been raised. diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java index d5f385b..c8a4593 100755 --- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java +++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java @@ -47,6 +47,7 @@ import android.util.Log; import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.util.Arrays; public class PackageManagerTests extends AndroidTestCase { private static final boolean localLOGV = true; @@ -2838,6 +2839,164 @@ public class PackageManagerTests extends AndroidTestCase { installFromRawResource("install.apk", rapk2, PackageManager.INSTALL_REPLACE_EXISTING, true, fail, retCode, PackageInfo.INSTALL_LOCATION_UNSPECIFIED); } + + @LargeTest + public void testPackageObbPaths_Nonexistent() { + try { + final PackageManager pm = getPm(); + + // Invalid Java package name. + pm.getPackageObbPaths("=non-existent"); + + fail("Should not be able to get package OBB paths for non-existent package"); + } catch (IllegalArgumentException e) { + // pass + } + } + + @LargeTest + public void testPackageObbPaths_Initial() { + InstallParams ip = sampleInstallFromRawResource(PackageManager.INSTALL_INTERNAL, false); + + try { + final PackageManager pm = getPm(); + + assertEquals("Initial obb paths should be null", + null, pm.getPackageObbPaths(ip.pkg.packageName)); + } finally { + cleanUpInstall(ip); + } + } + + @LargeTest + public void testPackageObbPaths_Null() { + InstallParams ip = sampleInstallFromRawResource(PackageManager.INSTALL_INTERNAL, false); + + try { + final PackageManager pm = getPm(); + + pm.setPackageObbPaths(ip.pkg.packageName, null); + + assertEquals("Returned paths should be null", + null, pm.getPackageObbPaths(ip.pkg.packageName)); + } finally { + cleanUpInstall(ip); + } + } + + @LargeTest + public void testPackageObbPaths_Empty() { + InstallParams ip = sampleInstallFromRawResource(PackageManager.INSTALL_INTERNAL, false); + + try { + final PackageManager pm = getPm(); + + final String[] paths = new String[0]; + + pm.setPackageObbPaths(ip.pkg.packageName, paths); + + assertEquals("Empty list should be interpreted as null", + null, pm.getPackageObbPaths(ip.pkg.packageName)); + } finally { + cleanUpInstall(ip); + } + } + + @LargeTest + public void testPackageObbPaths_Single() { + InstallParams ip = sampleInstallFromRawResource(PackageManager.INSTALL_INTERNAL, false); + + try { + final PackageManager pm = getPm(); + + final String[] paths = new String[] { + "/example/test", + }; + + pm.setPackageObbPaths(ip.pkg.packageName, paths.clone()); + + assertTrue("Previously set paths should be the same as the returned paths.", + Arrays.equals(paths, pm.getPackageObbPaths(ip.pkg.packageName))); + } finally { + cleanUpInstall(ip); + } + } + + @LargeTest + public void testPackageObbPaths_Multiple() { + InstallParams ip = sampleInstallFromRawResource(PackageManager.INSTALL_INTERNAL, false); + + try { + final PackageManager pm = getPm(); + + final String[] paths = new String[] { + "/example/test1", + "/example/test2", + }; + + pm.setPackageObbPaths(ip.pkg.packageName, paths.clone()); + + assertTrue("Previously set paths should be the same as the returned paths.", + Arrays.equals(paths, pm.getPackageObbPaths(ip.pkg.packageName))); + } finally { + cleanUpInstall(ip); + } + } + + @LargeTest + public void testPackageObbPaths_Twice() { + InstallParams ip = sampleInstallFromRawResource(PackageManager.INSTALL_INTERNAL, false); + + try { + final PackageManager pm = getPm(); + + final String[] paths = new String[] { + "/example/test1", + "/example/test2", + }; + + pm.setPackageObbPaths(ip.pkg.packageName, paths.clone()); + + assertTrue("Previously set paths should be the same as the returned paths.", + Arrays.equals(paths, pm.getPackageObbPaths(ip.pkg.packageName))); + + paths[0] = "/example/test3"; + pm.setPackageObbPaths(ip.pkg.packageName, paths.clone()); + + assertTrue("Previously set paths should be the same as the returned paths.", + Arrays.equals(paths, pm.getPackageObbPaths(ip.pkg.packageName))); + } finally { + cleanUpInstall(ip); + } + } + + @LargeTest + public void testPackageObbPaths_ReplacePackage() { + InstallParams ip = sampleInstallFromRawResource(PackageManager.INSTALL_INTERNAL, false); + + try { + final PackageManager pm = getPm(); + + final String[] paths = new String[] { + "/example/test1", + "/example/test2", + }; + + pm.setPackageObbPaths(ip.pkg.packageName, paths.clone()); + + Log.i(TAG, "Creating replaceReceiver"); + final GenericReceiver receiver = new ReplaceReceiver(ip.pkg.packageName); + + final int flags = PackageManager.INSTALL_REPLACE_EXISTING; + invokeInstallPackage(ip.packageURI, flags, receiver); + assertInstall(ip.pkg, flags, ip.pkg.installLocation); + + assertTrue("Previously set paths should be the same as the returned paths.", + Arrays.equals(paths, pm.getPackageObbPaths(ip.pkg.packageName))); + } finally { + cleanUpInstall(ip); + } + } /*---------- Recommended install location tests ----*/ /* * TODO's diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java index 7b49bc5..3f1a566 100644 --- a/graphics/java/android/graphics/BitmapFactory.java +++ b/graphics/java/android/graphics/BitmapFactory.java @@ -206,10 +206,20 @@ public class BitmapFactory { public boolean inNativeAlloc; /** + * If inPreferQualityOverSpeed is set to true, the decoder will try to + * decode the reconstructed image to a higher quality even at the + * expense of the decoding speed. Currently the field only affects JPEG + * decode, in the case of which a more accurate, but slightly slower, + * IDCT method will be used instead. + */ + public boolean inPreferQualityOverSpeed; + + /** * The resulting width of the bitmap, set independent of the state of * inJustDecodeBounds. However, if there is an error trying to decode, * outWidth will be set to -1. */ + public int outWidth; /** diff --git a/graphics/java/android/graphics/BitmapRegionDecoder.java b/graphics/java/android/graphics/BitmapRegionDecoder.java new file mode 100644 index 0000000..496e0c7 --- /dev/null +++ b/graphics/java/android/graphics/BitmapRegionDecoder.java @@ -0,0 +1,263 @@ +/* Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.graphics; + +import android.content.res.AssetManager; + +import java.io.BufferedInputStream; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * BitmapRegionDecoder can be used to decode a rectangle region from an image. + * BitmapRegionDecoder is particularly useful when an original image is large and + * you only need parts of the image. + * + * <p>To create a BitmapRegionDecoder, call newInstance(...). + * Given a BitmapRegionDecoder, users can call decodeRegion() repeatedly + * to get a decoded Bitmap of the specified region. + * + */ +public final class BitmapRegionDecoder { + private int mNativeBitmapRegionDecoder; + private boolean mRecycled; + + /** + * Create a BitmapRegionDecoder from the specified byte array. + * Currently only the JPEG and PNG formats are supported. + * + * @param data byte array of compressed image data. + * @param offset offset into data for where the decoder should begin + * parsing. + * @param length the number of bytes, beginning at offset, to parse + * @param isShareable If this is true, then the BitmapRegionDecoder may keep a + * shallow reference to the input. If this is false, + * then the BitmapRegionDecoder will explicitly make a copy of the + * input data, and keep that. Even if sharing is allowed, + * the implementation may still decide to make a deep + * copy of the input data. If an image is progressively encoded, + * allowing sharing may degrade the decoding speed. + * @return BitmapRegionDecoder, or null if the image data could not be decoded. + * @throws IOException if the image format is not supported or can not be decoded. + */ + public static BitmapRegionDecoder newInstance(byte[] data, + int offset, int length, boolean isShareable) throws IOException { + if ((offset | length) < 0 || data.length < offset + length) { + throw new ArrayIndexOutOfBoundsException(); + } + return nativeNewInstance(data, offset, length, isShareable); + } + + /** + * Create a BitmapRegionDecoder from the file descriptor. + * The position within the descriptor will not be changed when + * this returns, so the descriptor can be used again as is. + * Currently only the JPEG and PNG formats are supported. + * + * @param fd The file descriptor containing the data to decode + * @param isShareable If this is true, then the BitmapRegionDecoder may keep a + * shallow reference to the input. If this is false, + * then the BitmapRegionDecoder will explicitly make a copy of the + * input data, and keep that. Even if sharing is allowed, + * the implementation may still decide to make a deep + * copy of the input data. If an image is progressively encoded, + * allowing sharing may degrade the decoding speed. + * @return BitmapRegionDecoder, or null if the image data could not be decoded. + * @throws IOException if the image format is not supported or can not be decoded. + */ + public static BitmapRegionDecoder newInstance( + FileDescriptor fd, boolean isShareable) throws IOException { + return nativeNewInstance(fd, isShareable); + } + + /** + * Create a BitmapRegionDecoder from an input stream. + * The stream's position will be where ever it was after the encoded data + * was read. + * Currently only the JPEG and PNG formats are supported. + * + * @param is The input stream that holds the raw data to be decoded into a + * BitmapRegionDecoder. + * @param isShareable If this is true, then the BitmapRegionDecoder may keep a + * shallow reference to the input. If this is false, + * then the BitmapRegionDecoder will explicitly make a copy of the + * input data, and keep that. Even if sharing is allowed, + * the implementation may still decide to make a deep + * copy of the input data. If an image is progressively encoded, + * allowing sharing may degrade the decoding speed. + * @return BitmapRegionDecoder, or null if the image data could not be decoded. + * @throws IOException if the image format is not supported or can not be decoded. + */ + public static BitmapRegionDecoder newInstance(InputStream is, + boolean isShareable) throws IOException { + // we need mark/reset to work properly in JNI + + if (!is.markSupported()) { + is = new BufferedInputStream(is, 16 * 1024); + } + + if (is instanceof AssetManager.AssetInputStream) { + return nativeNewInstance( + ((AssetManager.AssetInputStream) is).getAssetInt(), + isShareable); + } else { + // pass some temp storage down to the native code. 1024 is made up, + // but should be large enough to avoid too many small calls back + // into is.read(...). + byte [] tempStorage = new byte[16 * 1024]; + return nativeNewInstance(is, tempStorage, isShareable); + } + } + + /** + * Create a BitmapRegionDecoder from a file path. + * Currently only the JPEG and PNG formats are supported. + * + * @param pathName complete path name for the file to be decoded. + * @param isShareable If this is true, then the BitmapRegionDecoder may keep a + * shallow reference to the input. If this is false, + * then the BitmapRegionDecoder will explicitly make a copy of the + * input data, and keep that. Even if sharing is allowed, + * the implementation may still decide to make a deep + * copy of the input data. If an image is progressively encoded, + * allowing sharing may degrade the decoding speed. + * @return BitmapRegionDecoder, or null if the image data could not be decoded. + * @throws IOException if the image format is not supported or can not be decoded. + */ + public static BitmapRegionDecoder newInstance(String pathName, + boolean isShareable) throws IOException { + BitmapRegionDecoder decoder = null; + InputStream stream = null; + + try { + stream = new FileInputStream(pathName); + decoder = newInstance(stream, isShareable); + } finally { + if (stream != null) { + try { + stream.close(); + } catch (IOException e) { + // do nothing here + } + } + } + return decoder; + } + + /* Private constructor that must receive an already allocated native + region decoder int (pointer). + + This can be called from JNI code. + */ + private BitmapRegionDecoder(int decoder) { + mNativeBitmapRegionDecoder = decoder; + mRecycled = false; + } + + /** + * Decodes a rectangle region in the image specified by rect. + * + * @param rect The rectangle that specified the region to be decode. + * @param options null-ok; Options that control downsampling. + * inPurgeable is not supported. + * @return The decoded bitmap, or null if the image data could not be + * decoded. + */ + public Bitmap decodeRegion(Rect rect, BitmapFactory.Options options) { + checkRecycled("decodeRegion called on recycled region decoder"); + if (rect.left < 0 || rect.top < 0 || rect.right > getWidth() + || rect.bottom > getHeight()) + throw new IllegalArgumentException("rectangle is not inside the image"); + return nativeDecodeRegion(mNativeBitmapRegionDecoder, rect.left, rect.top, + rect.right - rect.left, rect.bottom - rect.top, options); + } + + /** Returns the original image's width */ + public int getWidth() { + checkRecycled("getWidth called on recycled region decoder"); + return nativeGetWidth(mNativeBitmapRegionDecoder); + } + + /** Returns the original image's height */ + public int getHeight() { + checkRecycled("getHeight called on recycled region decoder"); + return nativeGetHeight(mNativeBitmapRegionDecoder); + } + + /** + * Frees up the memory associated with this region decoder, and mark the + * region decoder as "dead", meaning it will throw an exception if decodeRegion(), + * getWidth() or getHeight() is called. + * + * <p>This operation cannot be reversed, so it should only be called if you are + * sure there are no further uses for the region decoder. This is an advanced call, + * and normally need not be called, since the normal GC process will free up this + * memory when there are no more references to this region decoder. + */ + public void recycle() { + if (!mRecycled) { + nativeClean(mNativeBitmapRegionDecoder); + mRecycled = true; + } + } + + /** + * Returns true if this region decoder has been recycled. + * If so, then it is an error to try use its method. + * + * @return true if the region decoder has been recycled + */ + public final boolean isRecycled() { + return mRecycled; + } + + /** + * Called by methods that want to throw an exception if the region decoder + * has already been recycled. + */ + private void checkRecycled(String errorMessage) { + if (mRecycled) { + throw new IllegalStateException(errorMessage); + } + } + + @Override + protected void finalize() throws Throwable { + try { + recycle(); + } finally { + super.finalize(); + } + } + + private static native Bitmap nativeDecodeRegion(int lbm, + int start_x, int start_y, int width, int height, + BitmapFactory.Options options); + private static native int nativeGetWidth(int lbm); + private static native int nativeGetHeight(int lbm); + private static native void nativeClean(int lbm); + + private static native BitmapRegionDecoder nativeNewInstance( + byte[] data, int offset, int length, boolean isShareable); + private static native BitmapRegionDecoder nativeNewInstance( + FileDescriptor fd, boolean isShareable); + private static native BitmapRegionDecoder nativeNewInstance( + InputStream is, byte[] storage, boolean isShareable); + private static native BitmapRegionDecoder nativeNewInstance( + int asset, boolean isShareable); +} diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp index f6c55e4..ad9a94f 100644 --- a/libs/utils/Threads.cpp +++ b/libs/utils/Threads.cpp @@ -774,6 +774,9 @@ int Thread::_threadLoop(void* user) self->mExitPending = true; self->mLock.lock(); self->mRunning = false; + // clear thread ID so that requestExitAndWait() does not exit if + // called by a new thread using the same thread ID as this one. + self->mThread = thread_id_t(-1); self->mThreadExitedCondition.broadcast(); self->mLock.unlock(); break; diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_flightmode.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_flightmode.png Binary files differindex 01c7e2a..6bc6201 100644 --- a/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_flightmode.png +++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_flightmode.png diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_flightmode.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_flightmode.png Binary files differindex 4f0d1f1..aa2000e 100644 --- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_flightmode.png +++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_flightmode.png diff --git a/services/java/com/android/server/IntentResolver.java b/services/java/com/android/server/IntentResolver.java index e47de13..a8b2840 100644 --- a/services/java/com/android/server/IntentResolver.java +++ b/services/java/com/android/server/IntentResolver.java @@ -346,7 +346,7 @@ public class IntentResolver<F extends IntentFilter, R extends Object> { int num = 0; while (i.hasNext()) { - String name = (String)i.next(); + String name = i.next(); num++; if (localLOGV) Slog.v(TAG, prefix + name); String baseName = name; @@ -395,7 +395,7 @@ public class IntentResolver<F extends IntentFilter, R extends Object> { int num = 0; while (i.hasNext()) { - String name = (String)i.next(); + String name = i.next(); num++; if (localLOGV) Slog.v(TAG, prefix + name); String baseName = name; @@ -534,8 +534,8 @@ public class IntentResolver<F extends IntentFilter, R extends Object> { // Sorts a List of IntentFilter objects into descending priority order. private static final Comparator mResolvePrioritySorter = new Comparator() { public int compare(Object o1, Object o2) { - float q1 = ((IntentFilter)o1).getPriority(); - float q2 = ((IntentFilter)o2).getPriority(); + final int q1 = ((IntentFilter) o1).getPriority(); + final int q2 = ((IntentFilter) o2).getPriority(); return (q1 > q2) ? -1 : ((q1 < q2) ? 1 : 0); } }; diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java index 714bb1a..d00c043 100644 --- a/services/java/com/android/server/PackageManagerService.java +++ b/services/java/com/android/server/PackageManagerService.java @@ -102,6 +102,7 @@ import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; +import java.lang.reflect.Array; import java.security.NoSuchAlgorithmException; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -2881,13 +2882,13 @@ class PackageManagerService extends IPackageManager.Stub { SharedUserSetting suid = null; PackageSetting pkgSetting = null; - if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) == 0) { + if (!isSystemApp(pkg)) { // Only system apps can use these features. pkg.mOriginalPackages = null; pkg.mRealPackage = null; pkg.mAdoptPermissions = null; } - + synchronized (mPackages) { // Check all shared libraries and map to their actual file path. if (pkg.usesLibraries != null || pkg.usesOptionalLibraries != null) { @@ -4080,6 +4081,7 @@ class PackageManagerService extends IPackageManager.Stub { } public final void addActivity(PackageParser.Activity a, String type) { + final boolean systemApp = isSystemApp(a.info.applicationInfo); mActivities.put(a.getComponentName(), a); if (SHOW_INFO || Config.LOGV) Log.v( TAG, " " + type + " " + @@ -4088,6 +4090,11 @@ class PackageManagerService extends IPackageManager.Stub { int NI = a.intents.size(); for (int j=0; j<NI; j++) { PackageParser.ActivityIntentInfo intent = a.intents.get(j); + if (!systemApp && intent.getPriority() > 0 && "activity".equals(type)) { + intent.setPriority(0); + Log.w(TAG, "Package " + a.info.applicationInfo.packageName + " has activity " + + a.className + " with priority > 0, forcing to 0"); + } if (SHOW_INFO || Config.LOGV) { Log.v(TAG, " IntentFilter:"); intent.dump(new LogPrinter(Log.VERBOSE, TAG), " "); @@ -4566,16 +4573,52 @@ class PackageManagerService extends IPackageManager.Stub { mHandler.sendMessage(msg); } - public void setPackageObbPath(String packageName, String path) { + public void setPackageObbPaths(String packageName, String[] paths) { if (DEBUG_OBB) - Log.v(TAG, "Setting .obb path for " + packageName + " to: " + path); - PackageSetting pkgSetting; + Log.v(TAG, "Setting .obb paths for " + packageName + " to: " + Arrays.toString(paths)); final int uid = Binder.getCallingUid(); final int permission = mContext.checkCallingPermission( android.Manifest.permission.INSTALL_PACKAGES); final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED); + if (!allowedByPermission) { + throw new SecurityException("Permission denial: attempt to set .obb file from pid=" + + Binder.getCallingPid()); + } synchronized (mPackages) { - pkgSetting = mSettings.mPackages.get(packageName); + final PackageSetting pkgSetting = mSettings.mPackages.get(packageName); + if (pkgSetting == null) { + throw new IllegalArgumentException("Unknown package: " + packageName); + } + + if (paths != null) { + if (paths.length == 0) { + // Don't bother storing an empty array. + paths = null; + } else { + // Don't allow the caller to manipulate our copy of the + // list. + paths = paths.clone(); + } + } + + // Only write settings file if the new and old settings are not the + // same. + if (!Arrays.equals(paths, pkgSetting.obbPathStrings)) { + pkgSetting.obbPathStrings = paths; + mSettings.writeLP(); + } + } + } + + public String[] getPackageObbPaths(String packageName) { + if (DEBUG_OBB) + Log.v(TAG, "Getting .obb paths for " + packageName); + final int uid = Binder.getCallingUid(); + final int permission = mContext.checkCallingPermission( + android.Manifest.permission.INSTALL_PACKAGES); + final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED); + synchronized (mPackages) { + final PackageSetting pkgSetting = mSettings.mPackages.get(packageName); if (pkgSetting == null) { throw new IllegalArgumentException("Unknown package: " + packageName); } @@ -4584,8 +4627,7 @@ class PackageManagerService extends IPackageManager.Stub { + Binder.getCallingPid() + ", uid=" + uid + ", package uid=" + pkgSetting.userId); } - pkgSetting.obbPathString = path; - mSettings.writeLP(); + return pkgSetting.obbPathStrings; } } @@ -5952,6 +5994,10 @@ class PackageManagerService extends IPackageManager.Stub { return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; } + private static boolean isSystemApp(ApplicationInfo info) { + return (info.flags & ApplicationInfo.FLAG_SYSTEM) != 0; + } + private static boolean isUpdatedSystemApp(PackageParser.Package pkg) { return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0; } @@ -7154,7 +7200,7 @@ class PackageManagerService extends IPackageManager.Stub { pw.print(" codePath="); pw.println(ps.codePathString); pw.print(" resourcePath="); pw.println(ps.resourcePathString); pw.print(" nativeLibraryPath="); pw.println(ps.nativeLibraryPathString); - pw.print(" obbPath="); pw.println(ps.obbPathString); + pw.print(" obbPaths="); pw.println(Arrays.toString(ps.obbPathStrings)); pw.print(" versionCode="); pw.println(ps.versionCode); if (ps.pkg != null) { pw.print(" versionName="); pw.println(ps.pkg.mVersionName); @@ -7717,7 +7763,7 @@ class PackageManagerService extends IPackageManager.Stub { File resourcePath; String resourcePathString; String nativeLibraryPathString; - String obbPathString; + String[] obbPathStrings; long timeStamp; long firstInstallTime; long lastUpdateTime; @@ -8783,8 +8829,15 @@ class PackageManagerService extends IPackageManager.Stub { if (pkg.installerPackageName != null) { serializer.attribute(null, "installer", pkg.installerPackageName); } - if (pkg.obbPathString != null) { - serializer.attribute(null, "obbPath", pkg.obbPathString); + if (pkg.obbPathStrings != null && pkg.obbPathStrings.length > 0) { + int N = pkg.obbPathStrings.length; + serializer.startTag(null, "obbs"); + for (int i = 0; i < N; i++) { + serializer.startTag(null, "obb"); + serializer.attribute(null, "path", pkg.obbPathStrings[i]); + serializer.endTag(null, "obb"); + } + serializer.endTag(null, "obbs"); } pkg.signatures.writeXml(serializer, "sigs", mPastSignatures); if ((pkg.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) { @@ -9188,7 +9241,6 @@ class PackageManagerService extends IPackageManager.Stub { String codePathStr = null; String resourcePathStr = null; String nativeLibraryPathStr = null; - String obbPathStr = null; String systemStr = null; String installerPackageName = null; String uidError = null; @@ -9208,7 +9260,6 @@ class PackageManagerService extends IPackageManager.Stub { codePathStr = parser.getAttributeValue(null, "codePath"); resourcePathStr = parser.getAttributeValue(null, "resourcePath"); nativeLibraryPathStr = parser.getAttributeValue(null, "nativeLibraryPath"); - obbPathStr = parser.getAttributeValue(null, "obbPath"); version = parser.getAttributeValue(null, "version"); if (version != null) { try { @@ -9333,7 +9384,6 @@ class PackageManagerService extends IPackageManager.Stub { packageSetting.uidError = "true".equals(uidError); packageSetting.installerPackageName = installerPackageName; packageSetting.nativeLibraryPathString = nativeLibraryPathStr; - packageSetting.obbPathString = obbPathStr; final String enabledStr = parser.getAttributeValue(null, "enabled"); if (enabledStr != null) { if (enabledStr.equalsIgnoreCase("true")) { @@ -9381,6 +9431,8 @@ class PackageManagerService extends IPackageManager.Stub { readGrantedPermissionsLP(parser, packageSetting.grantedPermissions); packageSetting.permissionsFixed = true; + } else if (tagName.equals("obbs")) { + readObbPathsLP(packageSetting, parser); } else { reportSettingsProblem(Log.WARN, "Unknown element under <package>: " @@ -9585,6 +9637,34 @@ class PackageManagerService extends IPackageManager.Stub { } } + private void readObbPathsLP(PackageSettingBase packageSetting, XmlPullParser parser) + throws XmlPullParserException, IOException { + final List<String> obbPaths = new ArrayList<String>(); + final int outerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + + final String tagName = parser.getName(); + if (tagName.equals("obb")) { + final String path = parser.getAttributeValue(null, "path"); + obbPaths.add(path); + } else { + reportSettingsProblem(Log.WARN, "Unknown element under <obbs>: " + + parser.getName()); + } + XmlUtils.skipCurrentTag(parser); + } + if (obbPaths.size() == 0) { + return; + } else { + packageSetting.obbPathStrings = obbPaths.toArray(new String[obbPaths.size()]); + } + } + // Returns -1 if we could not find an available UserId to assign private int newUserIdLP(Object obj) { // Let's be stupidly inefficient for now... diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java index f0cbaa0..4a18b3e 100644 --- a/test-runner/src/android/test/mock/MockPackageManager.java +++ b/test-runner/src/android/test/mock/MockPackageManager.java @@ -490,8 +490,17 @@ public class MockPackageManager extends PackageManager { throw new UnsupportedOperationException(); } - @Override public void setPackageObbPath(String packageName, String path) { throw new UnsupportedOperationException(); } + + @Override + public void setPackageObbPaths(String packageName, String[] paths) { + throw new UnsupportedOperationException(); + } + + @Override + public String[] getPackageObbPaths(String packageName) { + throw new UnsupportedOperationException(); + } } |