diff options
99 files changed, 2413 insertions, 1113 deletions
diff --git a/api/current.txt b/api/current.txt index 7db6d5d..8716381 100644 --- a/api/current.txt +++ b/api/current.txt @@ -4496,6 +4496,7 @@ package android.app { public abstract class FragmentHostCallback extends android.app.FragmentContainer { ctor public FragmentHostCallback(android.content.Context, android.os.Handler, int); + method public void onAttachFragment(android.app.Fragment); method public void onDump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]); method public android.view.View onFindViewById(int); method public abstract E onGetHost(); @@ -9828,6 +9829,7 @@ package android.content.res { public final class Resources.Theme { method public void applyStyle(int, boolean); method public void dump(int, java.lang.String, java.lang.String); + method public int getChangingConfigurations(); method public android.graphics.drawable.Drawable getDrawable(int) throws android.content.res.Resources.NotFoundException; method public android.content.res.Resources getResources(); method public android.content.res.TypedArray obtainStyledAttributes(int[]); @@ -15607,6 +15609,16 @@ package android.media { field public static final int HEVCMainTierLevel62 = 16777216; // 0x1000000 field public static final int HEVCProfileMain = 1; // 0x1 field public static final int HEVCProfileMain10 = 2; // 0x2 + field public static final int MPEG2LevelH14 = 2; // 0x2 + field public static final int MPEG2LevelHL = 3; // 0x3 + field public static final int MPEG2LevelLL = 0; // 0x0 + field public static final int MPEG2LevelML = 1; // 0x1 + field public static final int MPEG2Profile422 = 2; // 0x2 + field public static final int MPEG2ProfileHigh = 5; // 0x5 + field public static final int MPEG2ProfileMain = 1; // 0x1 + field public static final int MPEG2ProfileSNR = 3; // 0x3 + field public static final int MPEG2ProfileSimple = 0; // 0x0 + field public static final int MPEG2ProfileSpatial = 4; // 0x4 field public static final int MPEG4Level0 = 1; // 0x1 field public static final int MPEG4Level0b = 2; // 0x2 field public static final int MPEG4Level1 = 4; // 0x4 diff --git a/api/system-current.txt b/api/system-current.txt index 3431a29..f11e113 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -110,6 +110,7 @@ package android { field public static final java.lang.String INTERNET = "android.permission.INTERNET"; field public static final java.lang.String INVOKE_CARRIER_SETUP = "android.permission.INVOKE_CARRIER_SETUP"; field public static final java.lang.String KILL_BACKGROUND_PROCESSES = "android.permission.KILL_BACKGROUND_PROCESSES"; + field public static final java.lang.String KILL_UID = "android.permission.KILL_UID"; field public static final java.lang.String LOCATION_HARDWARE = "android.permission.LOCATION_HARDWARE"; field public static final java.lang.String LOOP_RADIO = "android.permission.LOOP_RADIO"; field public static final java.lang.String MANAGE_ACCOUNTS = "android.permission.MANAGE_ACCOUNTS"; @@ -3671,6 +3672,7 @@ package android.app { method public static boolean isRunningInTestHarness(); method public static boolean isUserAMonkey(); method public void killBackgroundProcesses(java.lang.String); + method public void killUid(int, java.lang.String); method public void moveTaskToFront(int, int); method public void moveTaskToFront(int, int, android.os.Bundle); method public deprecated void restartPackage(java.lang.String); @@ -4586,6 +4588,7 @@ package android.app { public abstract class FragmentHostCallback extends android.app.FragmentContainer { ctor public FragmentHostCallback(android.content.Context, android.os.Handler, int); + method public void onAttachFragment(android.app.Fragment); method public void onDump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]); method public android.view.View onFindViewById(int); method public abstract E onGetHost(); @@ -10120,6 +10123,7 @@ package android.content.res { public final class Resources.Theme { method public void applyStyle(int, boolean); method public void dump(int, java.lang.String, java.lang.String); + method public int getChangingConfigurations(); method public android.graphics.drawable.Drawable getDrawable(int) throws android.content.res.Resources.NotFoundException; method public android.content.res.Resources getResources(); method public android.content.res.TypedArray obtainStyledAttributes(int[]); @@ -16827,6 +16831,16 @@ package android.media { field public static final int HEVCMainTierLevel62 = 16777216; // 0x1000000 field public static final int HEVCProfileMain = 1; // 0x1 field public static final int HEVCProfileMain10 = 2; // 0x2 + field public static final int MPEG2LevelH14 = 2; // 0x2 + field public static final int MPEG2LevelHL = 3; // 0x3 + field public static final int MPEG2LevelLL = 0; // 0x0 + field public static final int MPEG2LevelML = 1; // 0x1 + field public static final int MPEG2Profile422 = 2; // 0x2 + field public static final int MPEG2ProfileHigh = 5; // 0x5 + field public static final int MPEG2ProfileMain = 1; // 0x1 + field public static final int MPEG2ProfileSNR = 3; // 0x3 + field public static final int MPEG2ProfileSimple = 0; // 0x0 + field public static final int MPEG2ProfileSpatial = 4; // 0x4 field public static final int MPEG4Level0 = 1; // 0x1 field public static final int MPEG4Level0b = 2; // 0x2 field public static final int MPEG4Level1 = 4; // 0x4 diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index afbddd9..7260d10 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -6498,6 +6498,11 @@ public class Activity extends ContextThemeWrapper return (w == null) ? 0 : w.getAttributes().windowAnimations; } + @Override + public void onAttachFragment(Fragment fragment) { + Activity.this.onAttachFragment(fragment); + } + @Nullable @Override public View onFindViewById(int id) { diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 576a046..9bbb4be 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -16,8 +16,10 @@ package android.app; +import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.graphics.Canvas; import android.graphics.Matrix; @@ -26,6 +28,7 @@ import android.os.BatteryStats; import android.os.IBinder; import android.os.ParcelFileDescriptor; +import android.util.Log; import com.android.internal.app.ProcessStats; import com.android.internal.os.TransferPipe; import com.android.internal.util.FastPrintWriter; @@ -2396,7 +2399,24 @@ public class ActivityManager { } catch (RemoteException e) { } } - + + /** + * Kills the specified UID. + * @param uid The UID to kill. + * @param reason The reason for the kill. + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.KILL_UID) + public void killUid(int uid, String reason) { + try { + ActivityManagerNative.getDefault().killUid(uid, reason); + } catch (RemoteException e) { + Log.e(TAG, "Couldn't kill uid:" + uid, e); + } + } + /** * Have the system perform a force stop of everything associated with * the given application package. All processes that share its uid diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 6bbbf9e..5aa399b 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -941,7 +941,8 @@ public class AppOpsManager { * @hide */ public static int permissionToOpCode(String permission) { - return sPermToOp.get(permission); + Integer boxedOpCode = sPermToOp.get(permission); + return boxedOpCode != null ? boxedOpCode : OP_NONE; } /** diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java index 91d810e..40c5c64 100644 --- a/core/java/android/app/Fragment.java +++ b/core/java/android/app/Fragment.java @@ -1314,6 +1314,12 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene com.android.internal.R.styleable.Fragment_fragmentAllowReturnTransitionOverlap, true); } a.recycle(); + + final Activity hostActivity = mHost == null ? null : mHost.getActivity(); + if (hostActivity != null) { + mCalled = false; + onInflate(hostActivity, attrs, savedInstanceState); + } } /** diff --git a/core/java/android/app/FragmentHostCallback.java b/core/java/android/app/FragmentHostCallback.java index dad2c79..3e753f0 100644 --- a/core/java/android/app/FragmentHostCallback.java +++ b/core/java/android/app/FragmentHostCallback.java @@ -23,7 +23,6 @@ import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.util.ArrayMap; -import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; @@ -140,6 +139,14 @@ public abstract class FragmentHostCallback<E> extends FragmentContainer { return mWindowAnimations; } + /** + * Called when a {@link Fragment} is being attached to this host, immediately + * after the call to its {@link Fragment#onAttach(Context)} method and before + * {@link Fragment#onCreate(Bundle)}. + */ + public void onAttachFragment(Fragment fragment) { + } + @Nullable @Override public View onFindViewById(int id) { @@ -187,14 +194,6 @@ public abstract class FragmentHostCallback<E> extends FragmentContainer { } } - void onFragmentInflate(Fragment fragment, AttributeSet attrs, Bundle savedInstanceState) { - fragment.onInflate(mContext, attrs, savedInstanceState); - } - - void onFragmentAttach(Fragment fragment) { - fragment.onAttach(mContext); - } - void doLoaderStart() { if (mLoadersStarted) { return; diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java index 62436e9..6b5239d 100644 --- a/core/java/android/app/FragmentManager.java +++ b/core/java/android/app/FragmentManager.java @@ -844,13 +844,13 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate f.mFragmentManager = mParent != null ? mParent.mChildFragmentManager : mHost.getFragmentManagerImpl(); f.mCalled = false; - mHost.onFragmentAttach(f); + f.onAttach(mHost.getContext()); if (!f.mCalled) { throw new SuperNotCalledException("Fragment " + f + " did not call through to super.onAttach()"); } if (f.mParentFragment == null) { - mHost.onFragmentAttach(f); + mHost.onAttachFragment(f); } if (!f.mRetaining) { @@ -2107,7 +2107,7 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate fragment.mTag = tag; fragment.mInLayout = true; fragment.mFragmentManager = this; - mHost.onFragmentInflate(fragment, attrs, fragment.mSavedFragmentState); + fragment.onInflate(mHost.getContext(), attrs, fragment.mSavedFragmentState); addFragment(fragment, true); } else if (fragment.mInLayout) { // A fragment already exists and it is not one we restored from @@ -2124,7 +2124,7 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate // from last saved state), then give it the attributes to // initialize itself. if (!fragment.mRetaining) { - mHost.onFragmentInflate(fragment, attrs, fragment.mSavedFragmentState); + fragment.onInflate(mHost.getContext(), attrs, fragment.mSavedFragmentState); } } diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java index aea413d..470804d 100644 --- a/core/java/android/app/admin/DeviceAdminReceiver.java +++ b/core/java/android/app/admin/DeviceAdminReceiver.java @@ -231,7 +231,7 @@ public class DeviceAdminReceiver extends BroadcastReceiver { * device owner app. * * <p>The broadcast will be limited to the {@link DeviceAdminReceiver} component specified in - * the (@link DevicePolicyManager#EXTRA_PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME) field + * the {@link DevicePolicyManager#EXTRA_PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME} field * of the original intent or NFC bump that started the provisioning process. You will generally * handle this in {@link DeviceAdminReceiver#onReadyForUserInitialization}. * @@ -450,9 +450,9 @@ public class DeviceAdminReceiver extends BroadcastReceiver { * * <p>It is not assumed that the device initializer is finished when it returns from * this call, as it may do additional setup asynchronously. The device initializer must call - * {DevicePolicyManager#setUserEnabled(ComponentName admin)} when it has finished any additional - * setup (such as adding an account by using the {@link AccountManager}) in order for the user - * to be functional. + * {@link DevicePolicyManager#setUserEnabled(ComponentName admin)} when it has finished any + * additional setup (such as adding an account by using the {@link AccountManager}) in order for + * the user to be functional. * * @param context The running context as per {@link #onReceive}. * @param intent The received intent as per {@link #onReceive}. diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 9f71ea5..4b72dc3 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -412,7 +412,7 @@ public class DevicePolicyManager { = "android.app.action.MANAGED_PROFILE_PROVISIONED"; /** - * A boolean extra indicating whether device encryption is required as part of Device Owner + * A boolean extra indicating whether device encryption can be skipped as part of Device Owner * provisioning. * * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC_V2} that starts device owner diff --git a/core/java/android/app/trust/ITrustManager.aidl b/core/java/android/app/trust/ITrustManager.aidl index 17cff5c..32951d9 100644 --- a/core/java/android/app/trust/ITrustManager.aidl +++ b/core/java/android/app/trust/ITrustManager.aidl @@ -32,4 +32,5 @@ interface ITrustManager { void reportKeyguardShowingChanged(); boolean isDeviceLocked(int userId); boolean isDeviceSecure(int userId); + boolean hasUserAuthenticatedSinceBoot(int userId); } diff --git a/core/java/android/app/trust/TrustManager.java b/core/java/android/app/trust/TrustManager.java index b5c5317..8cab565 100644 --- a/core/java/android/app/trust/TrustManager.java +++ b/core/java/android/app/trust/TrustManager.java @@ -147,6 +147,23 @@ public class TrustManager { } } + /** + * Checks whether the specified user has been authenticated since the last boot. + * + * @param userId the user id of the user to check for + * @return true if the user has authenticated since boot, false otherwise + * + * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission. + */ + public boolean hasUserAuthenticatedSinceBoot(int userId) { + try { + return mService.hasUserAuthenticatedSinceBoot(userId); + } catch (RemoteException e) { + onError(e); + return false; + } + } + private void onError(Exception e) { Log.e(TAG, "Error while calling TrustManagerService", e); } diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index 16f6b1e..43cc63b 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -570,13 +570,16 @@ public class ActivityInfo extends ComponentInfo Configuration.NATIVE_CONFIG_DENSITY, // DENSITY Configuration.NATIVE_CONFIG_LAYOUTDIR, // LAYOUT DIRECTION }; - /** @hide + + /** * Convert Java change bits to native. + * + * @hide */ public static int activityInfoConfigToNative(int input) { int output = 0; - for (int i=0; i<CONFIG_NATIVE_BITS.length; i++) { - if ((input&(1<<i)) != 0) { + for (int i = 0; i < CONFIG_NATIVE_BITS.length; i++) { + if ((input & (1 << i)) != 0) { output |= CONFIG_NATIVE_BITS[i]; } } @@ -584,6 +587,21 @@ public class ActivityInfo extends ComponentInfo } /** + * Convert native change bits to Java. + * + * @hide + */ + public static int activityInfoConfigNativeToJava(int input) { + int output = 0; + for (int i = 0; i < CONFIG_NATIVE_BITS.length; i++) { + if ((input & CONFIG_NATIVE_BITS[i]) != 0) { + output |= (1 << i); + } + } + return output; + } + + /** * @hide * Unfortunately some developers (OpenFeint I am looking at you) have * compared the configChanges bit field against absolute values, so if we diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 0b24594..94b0223 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -96,9 +96,9 @@ interface IPackageManager { void removePermission(String name); - boolean grantPermission(String packageName, String permissionName, int userId); + void grantPermission(String packageName, String permissionName, int userId); - boolean revokePermission(String packageName, String permissionName, int userId); + void revokePermission(String packageName, String permissionName, int userId); boolean isProtectedBroadcast(String actionName); diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java index a176593..525059f 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -789,6 +789,7 @@ public final class AssetManager implements AutoCloseable { TypedValue outValue, boolean resolve); /*package*/ native static final void dumpTheme(long theme, int priority, String tag, String prefix); + /*package*/ native static final int getThemeChangingConfigurations(long theme); private native final long openXmlAssetNative(int cookie, String fileName); diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index 6e77e33..ae41b69 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -1731,6 +1731,19 @@ public class Resources { } /** + * Returns a bit mask of configuration changes that will impact this + * theme (and thus require completely reloading it). + * + * @return a bit mask of configuration changes, as defined by + * {@link ActivityInfo} + * @see ActivityInfo + */ + public int getChangingConfigurations() { + final int nativeChangingConfig = AssetManager.getThemeChangingConfigurations(mTheme); + return ActivityInfo.activityInfoConfigNativeToJava(nativeChangingConfig); + } + + /** * Print contents of this theme out to the log. For debugging only. * * @param priority The log priority to use. diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index f09f4d2..968cd72 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -692,7 +692,6 @@ public class ZenModeConfig implements Parcelable { .authority(SYSTEM_AUTHORITY) .appendPath(EVENT_PATH) .appendQueryParameter("calendar", Long.toString(event.calendar)) - .appendQueryParameter("attendance", Integer.toString(event.attendance)) .appendQueryParameter("reply", Integer.toString(event.reply)) .build(); } @@ -710,22 +709,16 @@ public class ZenModeConfig implements Parcelable { if (!isEvent) return null; final EventInfo rt = new EventInfo(); rt.calendar = tryParseLong(conditionId.getQueryParameter("calendar"), 0L); - rt.attendance = tryParseInt(conditionId.getQueryParameter("attendance"), 0); rt.reply = tryParseInt(conditionId.getQueryParameter("reply"), 0); return rt; } public static class EventInfo { - public static final int ATTENDANCE_REQUIRED_OR_OPTIONAL = 0; - public static final int ATTENDANCE_REQUIRED = 1; - public static final int ATTENDANCE_OPTIONAL = 2; - - public static final int REPLY_ANY = 0; - public static final int REPLY_ANY_EXCEPT_NO = 1; + public static final int REPLY_ANY_EXCEPT_NO = 0; + public static final int REPLY_YES_OR_MAYBE = 1; public static final int REPLY_YES = 2; public long calendar; // CalendarContract.Calendars._ID, or 0 for any - public int attendance; public int reply; @Override @@ -738,14 +731,12 @@ public class ZenModeConfig implements Parcelable { if (!(o instanceof EventInfo)) return false; final EventInfo other = (EventInfo) o; return calendar == other.calendar - && attendance == other.attendance && reply == other.reply; } public EventInfo copy() { final EventInfo rt = new EventInfo(); rt.calendar = calendar; - rt.attendance = attendance; rt.reply = reply; return rt; } diff --git a/core/java/android/text/Html.java b/core/java/android/text/Html.java index 7bebbfb..a55a08c 100644 --- a/core/java/android/text/Html.java +++ b/core/java/android/text/Html.java @@ -278,7 +278,7 @@ public class Html { if (style[j] instanceof TypefaceSpan) { String s = ((TypefaceSpan) style[j]).getFamily(); - if (s.equals("monospace")) { + if ("monospace".equals(s)) { out.append("<tt>"); } } diff --git a/core/java/android/view/DisplayListCanvas.java b/core/java/android/view/DisplayListCanvas.java index 48167c8..bb761f0 100644 --- a/core/java/android/view/DisplayListCanvas.java +++ b/core/java/android/view/DisplayListCanvas.java @@ -93,12 +93,6 @@ public class DisplayListCanvas extends Canvas { private static native long nCreateDisplayListCanvas(); - public static void setProperty(String name, String value) { - nSetProperty(name, value); - } - - private static native void nSetProperty(String name, String value); - /////////////////////////////////////////////////////////////////////////// // Canvas management /////////////////////////////////////////////////////////////////////////// diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java index 91e6d68..1fd7109 100644 --- a/core/java/android/view/ThreadedRenderer.java +++ b/core/java/android/view/ThreadedRenderer.java @@ -17,6 +17,7 @@ package android.view; import android.annotation.IntDef; +import android.annotation.NonNull; import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; @@ -412,6 +413,13 @@ public class ThreadedRenderer extends HardwareRenderer { nTrimMemory(level); } + public static void overrideProperty(@NonNull String name, @NonNull String value) { + if (name == null || value == null) { + throw new IllegalArgumentException("name and value must be non-null"); + } + nOverrideProperty(name, value); + } + public static void dumpProfileData(byte[] data, FileDescriptor fd) { nDumpProfileData(data, fd); } @@ -510,6 +518,7 @@ public class ThreadedRenderer extends HardwareRenderer { private static native void nDestroyHardwareResources(long nativeProxy); private static native void nTrimMemory(int level); + private static native void nOverrideProperty(String name, String value); private static native void nFence(long nativeProxy); private static native void nStopDrawing(long nativeProxy); diff --git a/core/java/com/android/internal/os/BinderInternal.java b/core/java/com/android/internal/os/BinderInternal.java index 240d9df..d77b998 100644 --- a/core/java/com/android/internal/os/BinderInternal.java +++ b/core/java/com/android/internal/os/BinderInternal.java @@ -20,6 +20,8 @@ import android.os.IBinder; import android.os.SystemClock; import android.util.EventLog; +import dalvik.system.VMRuntime; + import java.lang.ref.WeakReference; import java.util.ArrayList; @@ -96,7 +98,7 @@ public class BinderInternal { public static void forceGc(String reason) { EventLog.writeEvent(2741, reason); - Runtime.getRuntime().gc(); + VMRuntime.getRuntime().requestConcurrentGC(); } static void forceBinderGc() { diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp index 5c95f8a..8ae2e3b 100755 --- a/core/jni/android/graphics/Bitmap.cpp +++ b/core/jni/android/graphics/Bitmap.cpp @@ -1,3 +1,7 @@ +#define LOG_TAG "Bitmap" + +#include "Bitmap.h" + #include "Paint.h" #include "SkBitmap.h" #include "SkPixelRef.h" @@ -14,11 +18,322 @@ #include "android_util_Binder.h" #include "android_nio_utils.h" #include "CreateJavaOutputStreamAdaptor.h" +#include <Caches.h> #include "core_jni_helpers.h" #include <jni.h> +namespace android { + +class WrappedPixelRef : public SkPixelRef { +public: + WrappedPixelRef(Bitmap* wrapper, void* storage, + const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable) + : SkPixelRef(info) + , mBitmap(*wrapper) + , mStorage(storage) { + reconfigure(info, rowBytes, ctable); + } + + ~WrappedPixelRef() { + // Tell SkRefCnt that everything is as it expects by forcing + // the refcnt to 1 + internal_dispose_restore_refcnt_to_1(); + SkSafeUnref(mColorTable); + } + + void reconfigure(const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable) { + if (kIndex_8_SkColorType != info.colorType()) { + ctable = nullptr; + } + mRowBytes = rowBytes; + if (mColorTable != ctable) { + SkSafeUnref(mColorTable); + mColorTable = ctable; + SkSafeRef(mColorTable); + } + // Dirty hack is dirty + // TODO: Figure something out here, Skia's current design makes this + // really hard to work with. Skia really, really wants immutable objects, + // but with the nested-ref-count hackery going on that's just not + // feasible without going insane trying to figure it out + SkImageInfo* myInfo = const_cast<SkImageInfo*>(&this->info()); + *myInfo = info; + + // Docs say to only call this in the ctor, but we're going to call + // it anyway even if this isn't always the ctor. + // TODO: Fix this too as part of the above TODO + setPreLocked(mStorage, mRowBytes, mColorTable); + } + + // Can't mark as override since SkPixelRef::rowBytes isn't virtual + // but that's OK since we just want BitmapWrapper to be able to rely + // on calling rowBytes() on an unlocked pixelref, which it will be + // doing on a WrappedPixelRef type, not a SkPixelRef, so static + // dispatching will do what we want. + size_t rowBytes() const { return mRowBytes; } + SkColorTable* colorTable() const { return mColorTable; } + + bool hasHardwareMipMap() const { + return mHasHardwareMipMap; + } + + void setHasHardwareMipMap(bool hasMipMap) { + mHasHardwareMipMap = hasMipMap; + } + +protected: + virtual bool onNewLockPixels(LockRec* rec) override { + rec->fPixels = mStorage; + rec->fRowBytes = mRowBytes; + rec->fColorTable = mColorTable; + return true; + } + + virtual void onUnlockPixels() override { + // nothing + } + + virtual size_t getAllocatedSizeInBytes() const override { + return info().getSafeSize(mRowBytes); + } + +private: + Bitmap& mBitmap; + void* mStorage; + size_t mRowBytes = 0; + SkColorTable* mColorTable = nullptr; + bool mHasHardwareMipMap = false; + + virtual void internal_dispose() const override { + mBitmap.onStrongRefDestroyed(); + } +}; + +Bitmap::Bitmap(JNIEnv* env, jbyteArray storageObj, void* address, + const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable) + : mPixelStorageType(PixelStorageType::Java) { + env->GetJavaVM(&mPixelStorage.java.jvm); + mPixelStorage.java.jweakRef = env->NewWeakGlobalRef(storageObj); + mPixelStorage.java.jstrongRef = nullptr; + mPixelRef.reset(new WrappedPixelRef(this, address, info, rowBytes, ctable)); + // Note: this will trigger a call to onStrongRefDestroyed(), but + // we want the pixel ref to have a ref count of 0 at this point + mPixelRef->unref(); +} + +Bitmap::Bitmap(void* address, void* context, FreeFunc freeFunc, + const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable) + : mPixelStorageType(PixelStorageType::External) { + mPixelStorage.external.address = address; + mPixelStorage.external.context = context; + mPixelStorage.external.freeFunc = freeFunc; + mPixelRef.reset(new WrappedPixelRef(this, address, info, rowBytes, ctable)); + // Note: this will trigger a call to onStrongRefDestroyed(), but + // we want the pixel ref to have a ref count of 0 at this point + mPixelRef->unref(); +} + +Bitmap::~Bitmap() { + doFreePixels(); +} + +void Bitmap::freePixels() { + AutoMutex _lock(mLock); + if (mPinnedRefCount == 0) { + doFreePixels(); + mPixelStorageType = PixelStorageType::Invalid; + } +} + +void Bitmap::doFreePixels() { + switch (mPixelStorageType) { + case PixelStorageType::Invalid: + // already free'd, nothing to do + break; + case PixelStorageType::External: + mPixelStorage.external.freeFunc(mPixelStorage.external.address, + mPixelStorage.external.context); + break; + case PixelStorageType::Java: + JNIEnv* env = jniEnv(); + LOG_ALWAYS_FATAL_IF(mPixelStorage.java.jstrongRef, + "Deleting a bitmap wrapper while there are outstanding strong " + "references! mPinnedRefCount = %d", mPinnedRefCount); + env->DeleteWeakGlobalRef(mPixelStorage.java.jweakRef); + break; + } + + if (android::uirenderer::Caches::hasInstance()) { + android::uirenderer::Caches::getInstance().textureCache.releaseTexture( + mPixelRef->getStableID()); + } +} + +bool Bitmap::hasHardwareMipMap() { + return mPixelRef->hasHardwareMipMap(); +} + +void Bitmap::setHasHardwareMipMap(bool hasMipMap) { + mPixelRef->setHasHardwareMipMap(hasMipMap); +} + +const SkImageInfo& Bitmap::info() const { + assertValid(); + return mPixelRef->info(); +} + +size_t Bitmap::rowBytes() const { + return mPixelRef->rowBytes(); +} + +SkPixelRef* Bitmap::pixelRef() const { + assertValid(); + return mPixelRef.get(); +} + +void Bitmap::reconfigure(const SkImageInfo& info, size_t rowBytes, + SkColorTable* ctable) { + mPixelRef->reconfigure(info, rowBytes, ctable); +} + +void Bitmap::reconfigure(const SkImageInfo& info) { + mPixelRef->reconfigure(info, mPixelRef->rowBytes(), mPixelRef->colorTable()); +} + +void Bitmap::detachFromJava() { + bool disposeSelf; + { + android::AutoMutex _lock(mLock); + mAttachedToJava = false; + disposeSelf = shouldDisposeSelfLocked(); + } + if (disposeSelf) { + delete this; + } +} + +bool Bitmap::shouldDisposeSelfLocked() { + return mPinnedRefCount == 0 && !mAttachedToJava; +} + +JNIEnv* Bitmap::jniEnv() { + JNIEnv* env; + auto success = mPixelStorage.java.jvm->GetEnv((void**)&env, JNI_VERSION_1_6); + LOG_ALWAYS_FATAL_IF(success != JNI_OK, + "Failed to get JNIEnv* from JVM: %p", mPixelStorage.java.jvm); + return env; +} + +void Bitmap::onStrongRefDestroyed() { + bool disposeSelf = false; + { + android::AutoMutex _lock(mLock); + if (mPinnedRefCount > 0) { + mPinnedRefCount--; + if (mPinnedRefCount == 0) { + unpinPixelsLocked(); + disposeSelf = shouldDisposeSelfLocked(); + } + } + } + if (disposeSelf) { + delete this; + } +} + +void Bitmap::pinPixelsLocked() { + switch (mPixelStorageType) { + case PixelStorageType::Invalid: + LOG_ALWAYS_FATAL("Cannot pin invalid pixels!"); + break; + case PixelStorageType::External: + // Nothing to do + break; + case PixelStorageType::Java: { + JNIEnv* env = jniEnv(); + if (!mPixelStorage.java.jstrongRef) { + mPixelStorage.java.jstrongRef = reinterpret_cast<jbyteArray>( + env->NewGlobalRef(mPixelStorage.java.jweakRef)); + if (!mPixelStorage.java.jstrongRef) { + LOG_ALWAYS_FATAL("Failed to acquire strong reference to pixels"); + } + } + break; + } + } +} + +void Bitmap::unpinPixelsLocked() { + switch (mPixelStorageType) { + case PixelStorageType::Invalid: + LOG_ALWAYS_FATAL("Cannot unpin invalid pixels!"); + break; + case PixelStorageType::External: + // Don't need to do anything + break; + case PixelStorageType::Java: { + JNIEnv* env = jniEnv(); + if (mPixelStorage.java.jstrongRef) { + env->DeleteGlobalRef(mPixelStorage.java.jstrongRef); + mPixelStorage.java.jstrongRef = nullptr; + } + break; + } + } +} + +void Bitmap::getSkBitmap(SkBitmap* outBitmap) { + assertValid(); + android::AutoMutex _lock(mLock); + mPixelRef->ref(); + if (mPixelRef->unique()) { + // We just restored this from 0, pin the pixels and inc the strong count + // Note that there *might be* an incoming onStrongRefDestroyed from whatever + // last unref'd + pinPixelsLocked(); + mPinnedRefCount++; + } + // Safe because mPixelRef is a WrappedPixelRef type, otherwise rowBytes() + // would require locking the pixels first. + outBitmap->setInfo(mPixelRef->info(), mPixelRef->rowBytes()); + outBitmap->setPixelRef(mPixelRef.get())->unref(); + outBitmap->setHasHardwareMipMap(hasHardwareMipMap()); +} + +void Bitmap::assertValid() const { + LOG_ALWAYS_FATAL_IF(mPixelStorageType == PixelStorageType::Invalid, + "Error, cannot access an invalid/free'd bitmap here!"); +} + +} // namespace android + +using namespace android; + +// Convenience class that does not take a global ref on the pixels, relying +// on the caller already having a local JNI ref +class LocalScopedBitmap { +public: + LocalScopedBitmap(jlong bitmapHandle) + : mBitmap(reinterpret_cast<Bitmap*>(bitmapHandle)) {} + + Bitmap* operator->() { + return mBitmap; + } + + void* pixels() { + return mBitmap->pixelRef()->pixels(); + } + + bool valid() { + return mBitmap && mBitmap->valid(); + } + +private: + Bitmap* mBitmap; +}; + /////////////////////////////////////////////////////////////////////////////// // Conversions to/from SkColor, for get/setPixels, and the create method, which // is basically like setPixels @@ -328,8 +643,8 @@ static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors, SkBitmap bitmap; bitmap.setInfo(SkImageInfo::Make(width, height, colorType, kPremul_SkAlphaType)); - jbyteArray buff = GraphicsJNI::allocateJavaPixelRef(env, &bitmap, NULL); - if (NULL == buff) { + Bitmap* nativeBitmap = GraphicsJNI::allocateJavaPixelRef(env, &bitmap, NULL); + if (!nativeBitmap) { return NULL; } @@ -338,39 +653,41 @@ static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors, 0, 0, width, height, bitmap); } - return GraphicsJNI::createBitmap(env, new SkBitmap(bitmap), buff, - getPremulBitmapCreateFlags(isMutable), NULL, NULL); + return GraphicsJNI::createBitmap(env, nativeBitmap, + getPremulBitmapCreateFlags(isMutable)); } static jobject Bitmap_copy(JNIEnv* env, jobject, jlong srcHandle, jint dstConfigHandle, jboolean isMutable) { - const SkBitmap* src = reinterpret_cast<SkBitmap*>(srcHandle); + SkBitmap src; + reinterpret_cast<Bitmap*>(srcHandle)->getSkBitmap(&src); SkColorType dstCT = GraphicsJNI::legacyBitmapConfigToColorType(dstConfigHandle); SkBitmap result; JavaPixelAllocator allocator(env); - if (!src->copyTo(&result, dstCT, &allocator)) { + if (!src.copyTo(&result, dstCT, &allocator)) { return NULL; } - return GraphicsJNI::createBitmap(env, new SkBitmap(result), allocator.getStorageObj(), - getPremulBitmapCreateFlags(isMutable), NULL, NULL); + Bitmap* bitmap = allocator.getStorageObjAndReset(); + return GraphicsJNI::createBitmap(env, bitmap, + getPremulBitmapCreateFlags(isMutable)); } static void Bitmap_destructor(JNIEnv* env, jobject, jlong bitmapHandle) { - SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); - delete bitmap; + LocalScopedBitmap bitmap(bitmapHandle); + bitmap->detachFromJava(); } static jboolean Bitmap_recycle(JNIEnv* env, jobject, jlong bitmapHandle) { - SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); - bitmap->setPixels(NULL, NULL); + LocalScopedBitmap bitmap(bitmapHandle); + bitmap->freePixels(); return JNI_TRUE; } static void Bitmap_reconfigure(JNIEnv* env, jobject clazz, jlong bitmapHandle, jint width, jint height, jint configHandle, jint allocSize, jboolean requestPremul) { - SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); + LocalScopedBitmap bitmap(bitmapHandle); SkColorType colorType = GraphicsJNI::legacyBitmapConfigToColorType(configHandle); // ARGB_4444 is a deprecated format, convert automatically to 8888 @@ -383,11 +700,9 @@ static void Bitmap_reconfigure(JNIEnv* env, jobject clazz, jlong bitmapHandle, doThrowIAE(env, "Bitmap not large enough to support new configuration"); return; } - SkPixelRef* ref = bitmap->pixelRef(); - ref->ref(); SkAlphaType alphaType; - if (bitmap->colorType() != kRGB_565_SkColorType - && bitmap->alphaType() == kOpaque_SkAlphaType) { + if (bitmap->info().colorType() != kRGB_565_SkColorType + && bitmap->info().alphaType() == kOpaque_SkAlphaType) { // If the original bitmap was set to opaque, keep that setting, unless it // was 565, which is required to be opaque. alphaType = kOpaque_SkAlphaType; @@ -395,22 +710,7 @@ static void Bitmap_reconfigure(JNIEnv* env, jobject clazz, jlong bitmapHandle, // Otherwise respect the premultiplied request. alphaType = requestPremul ? kPremul_SkAlphaType : kUnpremul_SkAlphaType; } - bitmap->setInfo(SkImageInfo::Make(width, height, colorType, alphaType)); - // FIXME: Skia thinks of an SkPixelRef as having a constant SkImageInfo (except for - // its alphatype), so it would make more sense from Skia's perspective to create a - // new SkPixelRef. That said, libhwui uses the pointer to the SkPixelRef as a key - // for its cache, so it won't realize this is the same Java Bitmap. - SkImageInfo& info = const_cast<SkImageInfo&>(ref->info()); - // Use the updated from the SkBitmap, which may have corrected an invalid alphatype. - // (e.g. 565 non-opaque) - info = bitmap->info(); - bitmap->setPixelRef(ref); - - // notifyPixelsChanged will increment the generation ID even though the actual pixel data - // hasn't been touched. This signals the renderer that the bitmap (including width, height, - // colortype and alphatype) has changed. - ref->notifyPixelsChanged(); - ref->unref(); + bitmap->reconfigure(SkImageInfo::Make(width, height, colorType, alphaType)); } // These must match the int values in Bitmap.java @@ -423,7 +723,8 @@ enum JavaEncodeFormat { static jboolean Bitmap_compress(JNIEnv* env, jobject clazz, jlong bitmapHandle, jint format, jint quality, jobject jstream, jbyteArray jstorage) { - SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); + + LocalScopedBitmap bitmap(bitmapHandle); SkImageEncoder::Type fm; switch (format) { @@ -440,92 +741,92 @@ static jboolean Bitmap_compress(JNIEnv* env, jobject clazz, jlong bitmapHandle, return JNI_FALSE; } - bool success = false; - if (NULL != bitmap) { - SkAutoLockPixels alp(*bitmap); + if (!bitmap.valid()) { + return JNI_FALSE; + } - if (NULL == bitmap->getPixels()) { - return JNI_FALSE; - } + bool success = false; - SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage); - if (NULL == strm) { - return JNI_FALSE; - } + std::unique_ptr<SkWStream> strm(CreateJavaOutputStreamAdaptor(env, jstream, jstorage)); + if (!strm.get()) { + return JNI_FALSE; + } - SkImageEncoder* encoder = SkImageEncoder::Create(fm); - if (NULL != encoder) { - success = encoder->encodeStream(strm, *bitmap, quality); - delete encoder; - } - delete strm; + std::unique_ptr<SkImageEncoder> encoder(SkImageEncoder::Create(fm)); + if (encoder.get()) { + SkBitmap skbitmap; + bitmap->getSkBitmap(&skbitmap); + success = encoder->encodeStream(strm.get(), skbitmap, quality); } return success ? JNI_TRUE : JNI_FALSE; } static void Bitmap_erase(JNIEnv* env, jobject, jlong bitmapHandle, jint color) { - SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); - bitmap->eraseColor(color); + LocalScopedBitmap bitmap(bitmapHandle); + SkBitmap skBitmap; + bitmap->getSkBitmap(&skBitmap); + skBitmap.eraseColor(color); } static jint Bitmap_rowBytes(JNIEnv* env, jobject, jlong bitmapHandle) { - SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); + LocalScopedBitmap bitmap(bitmapHandle); return static_cast<jint>(bitmap->rowBytes()); } static jint Bitmap_config(JNIEnv* env, jobject, jlong bitmapHandle) { - SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); - return GraphicsJNI::colorTypeToLegacyBitmapConfig(bitmap->colorType()); + LocalScopedBitmap bitmap(bitmapHandle); + return GraphicsJNI::colorTypeToLegacyBitmapConfig(bitmap->info().colorType()); } static jint Bitmap_getGenerationId(JNIEnv* env, jobject, jlong bitmapHandle) { - SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); - return static_cast<jint>(bitmap->getGenerationID()); + LocalScopedBitmap bitmap(bitmapHandle); + return static_cast<jint>(bitmap->pixelRef()->getGenerationID()); } static jboolean Bitmap_isPremultiplied(JNIEnv* env, jobject, jlong bitmapHandle) { - SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); - if (bitmap->alphaType() == kPremul_SkAlphaType) { + LocalScopedBitmap bitmap(bitmapHandle); + if (bitmap->info().alphaType() == kPremul_SkAlphaType) { return JNI_TRUE; } return JNI_FALSE; } static jboolean Bitmap_hasAlpha(JNIEnv* env, jobject, jlong bitmapHandle) { - SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); - return !bitmap->isOpaque() ? JNI_TRUE : JNI_FALSE; + LocalScopedBitmap bitmap(bitmapHandle); + return !bitmap->info().isOpaque() ? JNI_TRUE : JNI_FALSE; } static void Bitmap_setHasAlpha(JNIEnv* env, jobject, jlong bitmapHandle, jboolean hasAlpha, jboolean requestPremul) { - SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); + LocalScopedBitmap bitmap(bitmapHandle); if (hasAlpha) { - bitmap->setAlphaType(requestPremul ? kPremul_SkAlphaType : kUnpremul_SkAlphaType); + bitmap->pixelRef()->changeAlphaType( + requestPremul ? kPremul_SkAlphaType : kUnpremul_SkAlphaType); } else { - bitmap->setAlphaType(kOpaque_SkAlphaType); + bitmap->pixelRef()->changeAlphaType(kOpaque_SkAlphaType); } } static void Bitmap_setPremultiplied(JNIEnv* env, jobject, jlong bitmapHandle, jboolean isPremul) { - SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); - if (!bitmap->isOpaque()) { + LocalScopedBitmap bitmap(bitmapHandle); + if (!bitmap->info().isOpaque()) { if (isPremul) { - bitmap->setAlphaType(kPremul_SkAlphaType); + bitmap->pixelRef()->changeAlphaType(kPremul_SkAlphaType); } else { - bitmap->setAlphaType(kUnpremul_SkAlphaType); + bitmap->pixelRef()->changeAlphaType(kUnpremul_SkAlphaType); } } } static jboolean Bitmap_hasMipMap(JNIEnv* env, jobject, jlong bitmapHandle) { - SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); + LocalScopedBitmap bitmap(bitmapHandle); return bitmap->hasHardwareMipMap() ? JNI_TRUE : JNI_FALSE; } static void Bitmap_setHasMipMap(JNIEnv* env, jobject, jlong bitmapHandle, jboolean hasMipMap) { - SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); + LocalScopedBitmap bitmap(bitmapHandle); bitmap->setHasHardwareMipMap(hasMipMap); } @@ -580,8 +881,8 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { } } - jbyteArray buffer = GraphicsJNI::allocateJavaPixelRef(env, bitmap.get(), ctable); - if (NULL == buffer) { + android::Bitmap* nativeBitmap = GraphicsJNI::allocateJavaPixelRef(env, bitmap.get(), ctable); + if (!nativeBitmap) { SkSafeUnref(ctable); return NULL; } @@ -593,6 +894,7 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { android::Parcel::ReadableBlob blob; android::status_t status = p->readBlob(size, &blob); if (status) { + nativeBitmap->detachFromJava(); doThrowRE(env, "Could not read bitmap from parcel blob."); return NULL; } @@ -603,7 +905,7 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { blob.release(); - return GraphicsJNI::createBitmap(env, bitmap.release(), buffer, + return GraphicsJNI::createBitmap(env, nativeBitmap, getPremulBitmapCreateFlags(isMutable), NULL, NULL, density); } @@ -611,24 +913,25 @@ static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject, jlong bitmapHandle, jboolean isMutable, jint density, jobject parcel) { - const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); if (parcel == NULL) { SkDebugf("------- writeToParcel null parcel\n"); return JNI_FALSE; } android::Parcel* p = android::parcelForJavaObject(env, parcel); + SkBitmap bitmap; + reinterpret_cast<Bitmap*>(bitmapHandle)->getSkBitmap(&bitmap); p->writeInt32(isMutable); - p->writeInt32(bitmap->colorType()); - p->writeInt32(bitmap->alphaType()); - p->writeInt32(bitmap->width()); - p->writeInt32(bitmap->height()); - p->writeInt32(bitmap->rowBytes()); + p->writeInt32(bitmap.colorType()); + p->writeInt32(bitmap.alphaType()); + p->writeInt32(bitmap.width()); + p->writeInt32(bitmap.height()); + p->writeInt32(bitmap.rowBytes()); p->writeInt32(density); - if (bitmap->colorType() == kIndex_8_SkColorType) { - SkColorTable* ctable = bitmap->getColorTable(); + if (bitmap.colorType() == kIndex_8_SkColorType) { + SkColorTable* ctable = bitmap.getColorTable(); if (ctable != NULL) { int count = ctable->count(); p->writeInt32(count); @@ -639,7 +942,7 @@ static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject, } } - size_t size = bitmap->getSize(); + size_t size = bitmap.getSize(); android::Parcel::WritableBlob blob; android::status_t status = p->writeBlob(size, &blob); @@ -648,14 +951,14 @@ static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject, return JNI_FALSE; } - bitmap->lockPixels(); - const void* pSrc = bitmap->getPixels(); + bitmap.lockPixels(); + const void* pSrc = bitmap.getPixels(); if (pSrc == NULL) { memset(blob.data(), 0, size); } else { memcpy(blob.data(), pSrc, size); } - bitmap->unlockPixels(); + bitmap.unlockPixels(); blob.release(); return JNI_TRUE; @@ -664,17 +967,17 @@ static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject, static jobject Bitmap_extractAlpha(JNIEnv* env, jobject clazz, jlong srcHandle, jlong paintHandle, jintArray offsetXY) { - const SkBitmap* src = reinterpret_cast<SkBitmap*>(srcHandle); + SkBitmap src; + reinterpret_cast<Bitmap*>(srcHandle)->getSkBitmap(&src); const android::Paint* paint = reinterpret_cast<android::Paint*>(paintHandle); SkIPoint offset; - SkBitmap* dst = new SkBitmap; + SkBitmap dst; JavaPixelAllocator allocator(env); - src->extractAlpha(dst, paint, &allocator, &offset); + src.extractAlpha(&dst, paint, &allocator, &offset); // If Skia can't allocate pixels for destination bitmap, it resets // it, that is set its pixels buffer to NULL, and zero width and height. - if (dst->getPixels() == NULL && src->getPixels() != NULL) { - delete dst; + if (dst.getPixels() == NULL && src.getPixels() != NULL) { doThrowOOME(env, "failed to allocate pixels for alpha"); return NULL; } @@ -685,53 +988,55 @@ static jobject Bitmap_extractAlpha(JNIEnv* env, jobject clazz, env->ReleaseIntArrayElements(offsetXY, array, 0); } - return GraphicsJNI::createBitmap(env, dst, allocator.getStorageObj(), - getPremulBitmapCreateFlags(true), NULL, NULL); + return GraphicsJNI::createBitmap(env, allocator.getStorageObjAndReset(), + getPremulBitmapCreateFlags(true)); } /////////////////////////////////////////////////////////////////////////////// static jint Bitmap_getPixel(JNIEnv* env, jobject, jlong bitmapHandle, jint x, jint y) { - const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); - SkAutoLockPixels alp(*bitmap); + SkBitmap bitmap; + reinterpret_cast<Bitmap*>(bitmapHandle)->getSkBitmap(&bitmap); + SkAutoLockPixels alp(bitmap); - ToColorProc proc = ChooseToColorProc(*bitmap); + ToColorProc proc = ChooseToColorProc(bitmap); if (NULL == proc) { return 0; } - const void* src = bitmap->getAddr(x, y); + const void* src = bitmap.getAddr(x, y); if (NULL == src) { return 0; } SkColor dst[1]; - proc(dst, src, 1, bitmap->getColorTable()); + proc(dst, src, 1, bitmap.getColorTable()); return static_cast<jint>(dst[0]); } static void Bitmap_getPixels(JNIEnv* env, jobject, jlong bitmapHandle, jintArray pixelArray, jint offset, jint stride, jint x, jint y, jint width, jint height) { - const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); - SkAutoLockPixels alp(*bitmap); + SkBitmap bitmap; + reinterpret_cast<Bitmap*>(bitmapHandle)->getSkBitmap(&bitmap); + SkAutoLockPixels alp(bitmap); - ToColorProc proc = ChooseToColorProc(*bitmap); + ToColorProc proc = ChooseToColorProc(bitmap); if (NULL == proc) { return; } - const void* src = bitmap->getAddr(x, y); + const void* src = bitmap.getAddr(x, y); if (NULL == src) { return; } - SkColorTable* ctable = bitmap->getColorTable(); + SkColorTable* ctable = bitmap.getColorTable(); jint* dst = env->GetIntArrayElements(pixelArray, NULL); SkColor* d = (SkColor*)dst + offset; while (--height >= 0) { proc(d, src, width, ctable); d += stride; - src = (void*)((const char*)src + bitmap->rowBytes()); + src = (void*)((const char*)src + bitmap.rowBytes()); } env->ReleaseIntArrayElements(pixelArray, dst, 0); } @@ -740,79 +1045,85 @@ static void Bitmap_getPixels(JNIEnv* env, jobject, jlong bitmapHandle, static void Bitmap_setPixel(JNIEnv* env, jobject, jlong bitmapHandle, jint x, jint y, jint colorHandle) { - const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); + SkBitmap bitmap; + reinterpret_cast<Bitmap*>(bitmapHandle)->getSkBitmap(&bitmap); SkColor color = static_cast<SkColor>(colorHandle); - SkAutoLockPixels alp(*bitmap); - if (NULL == bitmap->getPixels()) { + SkAutoLockPixels alp(bitmap); + if (NULL == bitmap.getPixels()) { return; } - FromColorProc proc = ChooseFromColorProc(*bitmap); + FromColorProc proc = ChooseFromColorProc(bitmap); if (NULL == proc) { return; } - proc(bitmap->getAddr(x, y), &color, 1, x, y); - bitmap->notifyPixelsChanged(); + proc(bitmap.getAddr(x, y), &color, 1, x, y); + bitmap.notifyPixelsChanged(); } static void Bitmap_setPixels(JNIEnv* env, jobject, jlong bitmapHandle, jintArray pixelArray, jint offset, jint stride, jint x, jint y, jint width, jint height) { - const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); + SkBitmap bitmap; + reinterpret_cast<Bitmap*>(bitmapHandle)->getSkBitmap(&bitmap); GraphicsJNI::SetPixels(env, pixelArray, offset, stride, - x, y, width, height, *bitmap); + x, y, width, height, bitmap); } static void Bitmap_copyPixelsToBuffer(JNIEnv* env, jobject, jlong bitmapHandle, jobject jbuffer) { - const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); - SkAutoLockPixels alp(*bitmap); - const void* src = bitmap->getPixels(); + SkBitmap bitmap; + reinterpret_cast<Bitmap*>(bitmapHandle)->getSkBitmap(&bitmap); + SkAutoLockPixels alp(bitmap); + const void* src = bitmap.getPixels(); if (NULL != src) { android::AutoBufferPointer abp(env, jbuffer, JNI_TRUE); // the java side has already checked that buffer is large enough - memcpy(abp.pointer(), src, bitmap->getSize()); + memcpy(abp.pointer(), src, bitmap.getSize()); } } static void Bitmap_copyPixelsFromBuffer(JNIEnv* env, jobject, jlong bitmapHandle, jobject jbuffer) { - SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); - SkAutoLockPixels alp(*bitmap); - void* dst = bitmap->getPixels(); + SkBitmap bitmap; + reinterpret_cast<Bitmap*>(bitmapHandle)->getSkBitmap(&bitmap); + SkAutoLockPixels alp(bitmap); + void* dst = bitmap.getPixels(); if (NULL != dst) { android::AutoBufferPointer abp(env, jbuffer, JNI_FALSE); // the java side has already checked that buffer is large enough - memcpy(dst, abp.pointer(), bitmap->getSize()); - bitmap->notifyPixelsChanged(); + memcpy(dst, abp.pointer(), bitmap.getSize()); + bitmap.notifyPixelsChanged(); } } static jboolean Bitmap_sameAs(JNIEnv* env, jobject, jlong bm0Handle, jlong bm1Handle) { - const SkBitmap* bm0 = reinterpret_cast<SkBitmap*>(bm0Handle); - const SkBitmap* bm1 = reinterpret_cast<SkBitmap*>(bm1Handle); - if (bm0->width() != bm1->width() || - bm0->height() != bm1->height() || - bm0->colorType() != bm1->colorType()) { + SkBitmap bm0; + SkBitmap bm1; + reinterpret_cast<Bitmap*>(bm0Handle)->getSkBitmap(&bm0); + reinterpret_cast<Bitmap*>(bm1Handle)->getSkBitmap(&bm1); + if (bm0.width() != bm1.width() || + bm0.height() != bm1.height() || + bm0.colorType() != bm1.colorType()) { return JNI_FALSE; } - SkAutoLockPixels alp0(*bm0); - SkAutoLockPixels alp1(*bm1); + SkAutoLockPixels alp0(bm0); + SkAutoLockPixels alp1(bm1); // if we can't load the pixels, return false - if (NULL == bm0->getPixels() || NULL == bm1->getPixels()) { + if (NULL == bm0.getPixels() || NULL == bm1.getPixels()) { return JNI_FALSE; } - if (bm0->colorType() == kIndex_8_SkColorType) { - SkColorTable* ct0 = bm0->getColorTable(); - SkColorTable* ct1 = bm1->getColorTable(); + if (bm0.colorType() == kIndex_8_SkColorType) { + SkColorTable* ct0 = bm0.getColorTable(); + SkColorTable* ct1 = bm1.getColorTable(); if (NULL == ct0 || NULL == ct1) { return JNI_FALSE; } @@ -829,16 +1140,16 @@ static jboolean Bitmap_sameAs(JNIEnv* env, jobject, jlong bm0Handle, // now compare each scanline. We can't do the entire buffer at once, // since we don't care about the pixel values that might extend beyond // the width (since the scanline might be larger than the logical width) - const int h = bm0->height(); - const size_t size = bm0->width() * bm0->bytesPerPixel(); + const int h = bm0.height(); + const size_t size = bm0.width() * bm0.bytesPerPixel(); for (int y = 0; y < h; y++) { // SkBitmap::getAddr(int, int) may return NULL due to unrecognized config // (ex: kRLE_Index8_Config). This will cause memcmp method to crash. Since bm0 // and bm1 both have pixel data() (have passed NULL == getPixels() check), // those 2 bitmaps should be valid (only unrecognized), we return JNI_FALSE // to warn user those 2 unrecognized config bitmaps may be different. - void *bm0Addr = bm0->getAddr(0, y); - void *bm1Addr = bm1->getAddr(0, y); + void *bm0Addr = bm0.getAddr(0, y); + void *bm1Addr = bm1.getAddr(0, y); if(bm0Addr == NULL || bm1Addr == NULL) { return JNI_FALSE; @@ -851,15 +1162,9 @@ static jboolean Bitmap_sameAs(JNIEnv* env, jobject, jlong bm0Handle, return JNI_TRUE; } -static void Bitmap_prepareToDraw(JNIEnv* env, jobject, jlong bitmapHandle) { - SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); - bitmap->lockPixels(); - bitmap->unlockPixels(); -} - static jlong Bitmap_refPixelRef(JNIEnv* env, jobject, jlong bitmapHandle) { - SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); - SkPixelRef* pixelRef = bitmap ? bitmap->pixelRef() : nullptr; + LocalScopedBitmap bitmap(bitmapHandle); + SkPixelRef* pixelRef = bitmap.valid() ? bitmap->pixelRef() : nullptr; SkSafeRef(pixelRef); return reinterpret_cast<jlong>(pixelRef); } @@ -902,7 +1207,6 @@ static JNINativeMethod gBitmapMethods[] = { { "nativeCopyPixelsFromBuffer", "(JLjava/nio/Buffer;)V", (void*)Bitmap_copyPixelsFromBuffer }, { "nativeSameAs", "(JJ)Z", (void*)Bitmap_sameAs }, - { "nativePrepareToDraw", "(J)V", (void*)Bitmap_prepareToDraw }, { "nativeRefPixelRef", "(J)J", (void*)Bitmap_refPixelRef }, }; diff --git a/core/jni/android/graphics/Bitmap.h b/core/jni/android/graphics/Bitmap.h new file mode 100644 index 0000000..d6e5c61 --- /dev/null +++ b/core/jni/android/graphics/Bitmap.h @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef BITMAP_H_ +#define BITMAP_H_ + +#include <jni.h> +#include <SkBitmap.h> +#include <SkColorTable.h> +#include <SkImageInfo.h> +#include <utils/Mutex.h> +#include <memory> + +namespace android { + +enum class PixelStorageType { + Invalid, + External, + Java, +}; + +class WrappedPixelRef; + +typedef void (*FreeFunc)(void* addr, void* context); + +/** + * Glue-thingy that deals with managing the interaction between the Java + * Bitmap object & SkBitmap along with trying to map a notion of strong/weak + * lifecycles onto SkPixelRef which only has strong counts to avoid requiring + * two GC passes to free the byte[] that backs a Bitmap. + * + * Since not all Bitmaps are byte[]-backed it also supports external allocations, + * which currently is used by screenshots to wrap a gralloc buffer. + */ +class Bitmap { +public: + Bitmap(JNIEnv* env, jbyteArray storageObj, void* address, + const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable); + Bitmap(void* address, void* context, FreeFunc freeFunc, + const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable); + + const SkImageInfo& info() const; + + // Returns nullptr if it is not backed by a jbyteArray + jbyteArray javaByteArray() const { + return mPixelStorageType == PixelStorageType::Java + ? mPixelStorage.java.jstrongRef : nullptr; + } + + int width() const { return info().width(); } + int height() const { return info().height(); } + size_t rowBytes() const; + SkPixelRef* pixelRef() const; + bool valid() const { return mPixelStorageType != PixelStorageType::Invalid; } + + void reconfigure(const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable); + void reconfigure(const SkImageInfo& info); + + void getSkBitmap(SkBitmap* outBitmap); + void detachFromJava(); + + void freePixels(); + + bool hasHardwareMipMap(); + void setHasHardwareMipMap(bool hasMipMap); + +private: + friend class WrappedPixelRef; + + ~Bitmap(); + void doFreePixels(); + void onStrongRefDestroyed(); + + void pinPixelsLocked(); + void unpinPixelsLocked(); + JNIEnv* jniEnv(); + bool shouldDisposeSelfLocked(); + void assertValid() const; + + android::Mutex mLock; + int mPinnedRefCount = 0; + std::unique_ptr<WrappedPixelRef> mPixelRef; + PixelStorageType mPixelStorageType; + bool mAttachedToJava = true; + + union { + struct { + void* address; + void* context; + FreeFunc freeFunc; + } external; + struct { + JavaVM* jvm; + jweak jweakRef; + jbyteArray jstrongRef; + } java; + } mPixelStorage; +}; + +} // namespace android + +#endif /* BITMAP_H_ */ diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp index d4069a1..cdd397d 100644 --- a/core/jni/android/graphics/BitmapFactory.cpp +++ b/core/jni/android/graphics/BitmapFactory.cpp @@ -156,13 +156,11 @@ private: class RecyclingPixelAllocator : public SkBitmap::Allocator { public: - RecyclingPixelAllocator(SkPixelRef* pixelRef, unsigned int size) - : mPixelRef(pixelRef), mSize(size) { - SkSafeRef(mPixelRef); + RecyclingPixelAllocator(android::Bitmap* bitmap, unsigned int size) + : mBitmap(bitmap), mSize(size) { } ~RecyclingPixelAllocator() { - SkSafeUnref(mPixelRef); } virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) { @@ -185,11 +183,9 @@ public: return false; } - // Create a new pixelref with the new ctable that wraps the previous pixelref - SkPixelRef* pr = new AndroidPixelRef(*static_cast<AndroidPixelRef*>(mPixelRef), - info, bitmap->rowBytes(), ctable); + mBitmap->reconfigure(info, bitmap->rowBytes(), ctable); + bitmap->setPixelRef(mBitmap->pixelRef()); - bitmap->setPixelRef(pr)->unref(); // since we're already allocated, we lockPixels right away // HeapAllocator/JavaPixelAllocator behaves this way too bitmap->lockPixels(); @@ -197,7 +193,7 @@ public: } private: - SkPixelRef* const mPixelRef; + android::Bitmap* const mBitmap; const unsigned int mSize; }; @@ -258,27 +254,24 @@ static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding decoder->setPreferQualityOverSpeed(preferQualityOverSpeed); decoder->setRequireUnpremultipliedColors(requireUnpremultiplied); - SkBitmap* outputBitmap = NULL; + android::Bitmap* reuseBitmap = nullptr; unsigned int existingBufferSize = 0; if (javaBitmap != NULL) { - outputBitmap = GraphicsJNI::getSkBitmapDeprecated(env, javaBitmap); - if (outputBitmap->isImmutable()) { + reuseBitmap = GraphicsJNI::getBitmap(env, javaBitmap); + if (reuseBitmap->pixelRef()->isImmutable()) { ALOGW("Unable to reuse an immutable bitmap as an image decoder target."); javaBitmap = NULL; - outputBitmap = NULL; + reuseBitmap = nullptr; } else { existingBufferSize = GraphicsJNI::getBitmapAllocationByteCount(env, javaBitmap); } } - SkAutoTDelete<SkBitmap> adb(outputBitmap == NULL ? new SkBitmap : NULL); - if (outputBitmap == NULL) outputBitmap = adb.get(); - NinePatchPeeker peeker(decoder); decoder->setPeeker(&peeker); JavaPixelAllocator javaAllocator(env); - RecyclingPixelAllocator recyclingAllocator(outputBitmap->pixelRef(), existingBufferSize); + RecyclingPixelAllocator recyclingAllocator(reuseBitmap, existingBufferSize); ScaleCheckingAllocator scaleCheckingAllocator(scale, existingBufferSize); SkBitmap::Allocator* outputAllocator = (javaBitmap != NULL) ? (SkBitmap::Allocator*)&recyclingAllocator : (SkBitmap::Allocator*)&javaAllocator; @@ -374,6 +367,7 @@ static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding } } + SkBitmap outputBitmap; if (willScale) { // This is weird so let me explain: we could use the scale parameter // directly, but for historical reasons this is how the corresponding @@ -388,26 +382,27 @@ static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding // FIXME: If the alphaType is kUnpremul and the image has alpha, the // colors may not be correct, since Skia does not yet support drawing // to/from unpremultiplied bitmaps. - outputBitmap->setInfo(SkImageInfo::Make(scaledWidth, scaledHeight, + outputBitmap.setInfo(SkImageInfo::Make(scaledWidth, scaledHeight, colorType, decodingBitmap.alphaType())); - if (!outputBitmap->tryAllocPixels(outputAllocator, NULL)) { + if (!outputBitmap.tryAllocPixels(outputAllocator, NULL)) { return nullObjectReturn("allocation failed for scaled bitmap"); } // If outputBitmap's pixels are newly allocated by Java, there is no need // to erase to 0, since the pixels were initialized to 0. if (outputAllocator != &javaAllocator) { - outputBitmap->eraseColor(0); + outputBitmap.eraseColor(0); } SkPaint paint; paint.setFilterQuality(kLow_SkFilterQuality); - SkCanvas canvas(*outputBitmap); + SkCanvas canvas(outputBitmap); canvas.scale(sx, sy); + canvas.drawARGB(0x00, 0x00, 0x00, 0x00); canvas.drawBitmap(decodingBitmap, 0.0f, 0.0f, &paint); } else { - outputBitmap->swap(decodingBitmap); + outputBitmap.swap(decodingBitmap); } if (padding) { @@ -422,22 +417,19 @@ static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding // if we get here, we're in kDecodePixels_Mode and will therefore // already have a pixelref installed. - if (outputBitmap->pixelRef() == NULL) { + if (outputBitmap.pixelRef() == NULL) { return nullObjectReturn("Got null SkPixelRef"); } if (!isMutable && javaBitmap == NULL) { // promise we will never change our pixels (great for sharing and pictures) - outputBitmap->setImmutable(); + outputBitmap.setImmutable(); } - // detach bitmap from its autodeleter, since we want to own it now - adb.detach(); - if (javaBitmap != NULL) { bool isPremultiplied = !requireUnpremultiplied; - GraphicsJNI::reinitBitmap(env, javaBitmap, outputBitmap, isPremultiplied); - outputBitmap->notifyPixelsChanged(); + GraphicsJNI::reinitBitmap(env, javaBitmap, outputBitmap.info(), isPremultiplied); + outputBitmap.notifyPixelsChanged(); // If a java bitmap was passed in for reuse, pass it back return javaBitmap; } @@ -447,7 +439,7 @@ static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding if (!requireUnpremultiplied) bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Premultiplied; // now create the java bitmap - return GraphicsJNI::createBitmap(env, outputBitmap, javaAllocator.getStorageObj(), + return GraphicsJNI::createBitmap(env, javaAllocator.getStorageObjAndReset(), bitmapCreateFlags, ninePatchChunk, ninePatchInsets, -1); } diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp index aeea808..08a3f6f 100644 --- a/core/jni/android/graphics/BitmapRegionDecoder.cpp +++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp @@ -212,26 +212,21 @@ static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, region.fTop = start_y; region.fRight = start_x + width; region.fBottom = start_y + height; - SkBitmap* bitmap = NULL; - SkAutoTDelete<SkBitmap> adb; + SkBitmap bitmap; if (tileBitmap != NULL) { // Re-use bitmap. - bitmap = GraphicsJNI::getSkBitmapDeprecated(env, tileBitmap); - } - if (bitmap == NULL) { - bitmap = new SkBitmap; - adb.reset(bitmap); + GraphicsJNI::getSkBitmap(env, tileBitmap, &bitmap); } - if (!brd->decodeRegion(bitmap, region, prefColorType, sampleSize)) { + if (!brd->decodeRegion(&bitmap, region, prefColorType, 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()); + 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? @@ -240,19 +235,16 @@ static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, } if (tileBitmap != NULL) { - bitmap->notifyPixelsChanged(); + bitmap.notifyPixelsChanged(); return tileBitmap; } - // detach bitmap from its autodeleter, since we want to own it now - adb.detach(); - JavaPixelAllocator* allocator = (JavaPixelAllocator*) decoder->getAllocator(); - jbyteArray buff = allocator->getStorageObjAndReset(); int bitmapCreateFlags = 0; if (!requireUnpremultiplied) bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Premultiplied; - return GraphicsJNI::createBitmap(env, bitmap, buff, bitmapCreateFlags, NULL, NULL, -1); + return GraphicsJNI::createBitmap(env, allocator->getStorageObjAndReset(), + bitmapCreateFlags); } static jint nativeGetHeight(JNIEnv* env, jobject, jlong brdHandle) { diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp index f793df1..0deb8cc 100644 --- a/core/jni/android/graphics/Graphics.cpp +++ b/core/jni/android/graphics/Graphics.cpp @@ -154,7 +154,7 @@ static jfieldID gPointF_xFieldID; static jfieldID gPointF_yFieldID; static jclass gBitmap_class; -static jfieldID gBitmap_skBitmapPtr; +static jfieldID gBitmap_nativePtr; static jmethodID gBitmap_constructorMethodID; static jmethodID gBitmap_reinitMethodID; static jmethodID gBitmap_getAllocationByteCountMethodID; @@ -338,27 +338,22 @@ SkColorType GraphicsJNI::legacyBitmapConfigToColorType(jint legacyConfig) { return static_cast<SkColorType>(gConfig2ColorType[legacyConfig]); } -SkBitmap* GraphicsJNI::getSkBitmapDeprecated(JNIEnv* env, jobject bitmap) { +android::Bitmap* GraphicsJNI::getBitmap(JNIEnv* env, jobject bitmap) { SkASSERT(env); SkASSERT(bitmap); SkASSERT(env->IsInstanceOf(bitmap, gBitmap_class)); - jlong bitmapHandle = env->GetLongField(bitmap, gBitmap_skBitmapPtr); - SkBitmap* b = reinterpret_cast<SkBitmap*>(bitmapHandle); + jlong bitmapHandle = env->GetLongField(bitmap, gBitmap_nativePtr); + android::Bitmap* b = reinterpret_cast<android::Bitmap*>(bitmapHandle); SkASSERT(b); return b; } void GraphicsJNI::getSkBitmap(JNIEnv* env, jobject bitmap, SkBitmap* outBitmap) { - // TODO: We have to copy from the existing bitmap due to rowBytes not - // being updated on the SkPixelRef at reconfigure time. This is a short term - // problem that will be fixed with the specialized wrapper - *outBitmap = *getSkBitmapDeprecated(env, bitmap); + getBitmap(env, bitmap)->getSkBitmap(outBitmap); } SkPixelRef* GraphicsJNI::getSkPixelRef(JNIEnv* env, jobject bitmap) { - jlong bitmapHandle = env->GetLongField(bitmap, gBitmap_skBitmapPtr); - SkBitmap* b = reinterpret_cast<SkBitmap*>(bitmapHandle); - return b->pixelRef(); + return getBitmap(env, bitmap)->pixelRef(); } SkColorType GraphicsJNI::getNativeBitmapColorType(JNIEnv* env, jobject jconfig) { @@ -396,47 +391,43 @@ SkRegion* GraphicsJNI::getNativeRegion(JNIEnv* env, jobject region) /////////////////////////////////////////////////////////////////////////////////////////// // Assert that bitmap's SkAlphaType is consistent with isPremultiplied. -static void assert_premultiplied(const SkBitmap& bitmap, bool isPremultiplied) { +static void assert_premultiplied(const SkImageInfo& info, bool isPremultiplied) { // kOpaque_SkAlphaType and kIgnore_SkAlphaType mean that isPremultiplied is // irrelevant. This just tests to ensure that the SkAlphaType is not // opposite of isPremultiplied. if (isPremultiplied) { - SkASSERT(bitmap.alphaType() != kUnpremul_SkAlphaType); + SkASSERT(info.alphaType() != kUnpremul_SkAlphaType); } else { - SkASSERT(bitmap.alphaType() != kPremul_SkAlphaType); + SkASSERT(info.alphaType() != kPremul_SkAlphaType); } } -jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, jbyteArray buffer, - int bitmapCreateFlags, jbyteArray ninePatchChunk, jobject ninePatchInsets, int density) -{ - SkASSERT(bitmap); - SkASSERT(bitmap->pixelRef()); - SkASSERT(!env->ExceptionCheck()); +jobject GraphicsJNI::createBitmap(JNIEnv* env, android::Bitmap* bitmap, + int bitmapCreateFlags, jbyteArray ninePatchChunk, jobject ninePatchInsets, + int density) { bool isMutable = bitmapCreateFlags & kBitmapCreateFlag_Mutable; bool isPremultiplied = bitmapCreateFlags & kBitmapCreateFlag_Premultiplied; - // The caller needs to have already set the alpha type properly, so the // native SkBitmap stays in sync with the Java Bitmap. - assert_premultiplied(*bitmap, isPremultiplied); + assert_premultiplied(bitmap->info(), isPremultiplied); jobject obj = env->NewObject(gBitmap_class, gBitmap_constructorMethodID, - reinterpret_cast<jlong>(bitmap), buffer, + reinterpret_cast<jlong>(bitmap), bitmap->javaByteArray(), bitmap->width(), bitmap->height(), density, isMutable, isPremultiplied, ninePatchChunk, ninePatchInsets); hasException(env); // For the side effect of logging. return obj; } -void GraphicsJNI::reinitBitmap(JNIEnv* env, jobject javaBitmap, SkBitmap* bitmap, +void GraphicsJNI::reinitBitmap(JNIEnv* env, jobject javaBitmap, const SkImageInfo& info, bool isPremultiplied) { // The caller needs to have already set the alpha type properly, so the // native SkBitmap stays in sync with the Java Bitmap. - assert_premultiplied(*bitmap, isPremultiplied); + assert_premultiplied(info, isPremultiplied); env->CallVoidMethod(javaBitmap, gBitmap_reinitMethodID, - bitmap->width(), bitmap->height(), isPremultiplied); + info.width(), info.height(), isPremultiplied); } int GraphicsJNI::getBitmapAllocationByteCount(JNIEnv* env, jobject javaBitmap) @@ -477,51 +468,6 @@ static JNIEnv* vm2env(JavaVM* vm) /////////////////////////////////////////////////////////////////////////////// -AndroidPixelRef::AndroidPixelRef(JNIEnv* env, const SkImageInfo& info, void* storage, - size_t rowBytes, jbyteArray storageObj, SkColorTable* ctable) : - SkMallocPixelRef(info, storage, rowBytes, ctable, (storageObj == NULL)), - fWrappedPixelRef(NULL) { - SkASSERT(storage); - SkASSERT(storageObj); - SkASSERT(env); - - if (env->GetJavaVM(&fVM) != JNI_OK) { - SkDebugf("------ [%p] env->GetJavaVM failed\n", env); - sk_throw(); - } - - fStorageObj = (jbyteArray) env->NewGlobalRef(storageObj); -} - -AndroidPixelRef::AndroidPixelRef(AndroidPixelRef& wrappedPixelRef, const SkImageInfo& info, - size_t rowBytes, SkColorTable* ctable) : - SkMallocPixelRef(info, wrappedPixelRef.getAddr(), rowBytes, ctable, false), - fWrappedPixelRef(wrappedPixelRef.fWrappedPixelRef ? - wrappedPixelRef.fWrappedPixelRef : &wrappedPixelRef) -{ - SkASSERT(fWrappedPixelRef); - SkSafeRef(fWrappedPixelRef); - - // don't need to initialize this, as all the relevant logic delegates to the wrapped ref - fStorageObj = NULL; -} - -AndroidPixelRef::~AndroidPixelRef() { - if (fWrappedPixelRef) { - SkSafeUnref(fWrappedPixelRef); - } else { - SkASSERT(fStorageObj); - JNIEnv* env = vm2env(fVM); - env->DeleteGlobalRef(fStorageObj); - } - - if (android::uirenderer::Caches::hasInstance()) { - android::uirenderer::Caches::getInstance().textureCache.releaseTexture(getStableID()); - } -} - -/////////////////////////////////////////////////////////////////////////////// - static bool computeAllocationSize(const SkBitmap& bitmap, size_t* size) { int32_t rowBytes32 = SkToS32(bitmap.rowBytes()); int64_t bigSize = (int64_t)bitmap.height() * rowBytes32; @@ -533,7 +479,7 @@ static bool computeAllocationSize(const SkBitmap& bitmap, size_t* size) { return true; } -jbyteArray GraphicsJNI::allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap, +android::Bitmap* GraphicsJNI::allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap, SkColorTable* ctable) { const SkImageInfo& info = bitmap->info(); if (info.fColorType == kUnknown_SkColorType) { @@ -562,13 +508,14 @@ jbyteArray GraphicsJNI::allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap, return NULL; } SkASSERT(addr); - SkPixelRef* pr = new AndroidPixelRef(env, info, (void*) addr, rowBytes, arrayObj, ctable); - bitmap->setPixelRef(pr)->unref(); + android::Bitmap* wrapper = new android::Bitmap(env, arrayObj, (void*) addr, + info, rowBytes, ctable); + wrapper->getSkBitmap(bitmap); // since we're already allocated, we lockPixels right away // HeapAllocator behaves this way too bitmap->lockPixels(); - return arrayObj; + return wrapper; } struct AndroidPixelRefContext { @@ -627,21 +574,22 @@ bool GraphicsJNI::allocatePixels(JNIEnv* env, SkBitmap* bitmap, SkColorTable* ct /////////////////////////////////////////////////////////////////////////////// -JavaPixelAllocator::JavaPixelAllocator(JNIEnv* env) - : fStorageObj(NULL), - fAllocCount(0) { - if (env->GetJavaVM(&fVM) != JNI_OK) { - SkDebugf("------ [%p] env->GetJavaVM failed\n", env); - sk_throw(); +JavaPixelAllocator::JavaPixelAllocator(JNIEnv* env) { + LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&mJavaVM) != JNI_OK, + "env->GetJavaVM failed"); +} + +JavaPixelAllocator::~JavaPixelAllocator() { + if (mStorage) { + mStorage->detachFromJava(); } } bool JavaPixelAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) { - JNIEnv* env = vm2env(fVM); + JNIEnv* env = vm2env(mJavaVM); - fStorageObj = GraphicsJNI::allocateJavaPixelRef(env, bitmap, ctable); - fAllocCount += 1; - return fStorageObj != NULL; + mStorage = GraphicsJNI::allocateJavaPixelRef(env, bitmap, ctable); + return mStorage != nullptr; } //////////////////////////////////////////////////////////////////////////////// @@ -687,7 +635,7 @@ int register_android_graphics_Graphics(JNIEnv* env) gPointF_yFieldID = getFieldIDCheck(env, gPointF_class, "y", "F"); gBitmap_class = make_globalref(env, "android/graphics/Bitmap"); - gBitmap_skBitmapPtr = getFieldIDCheck(env, gBitmap_class, "mSkBitmapPtr", "J"); + gBitmap_nativePtr = getFieldIDCheck(env, gBitmap_class, "mNativePtr", "J"); gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "<init>", "(J[BIIIZZ[BLandroid/graphics/NinePatch$InsetStruct;)V"); gBitmap_reinitMethodID = env->GetMethodID(gBitmap_class, "reinit", "(IIZ)V"); gBitmap_getAllocationByteCountMethodID = env->GetMethodID(gBitmap_class, "getAllocationByteCount", "()I"); diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h index 8eb43f8..e748bac 100644 --- a/core/jni/android/graphics/GraphicsJNI.h +++ b/core/jni/android/graphics/GraphicsJNI.h @@ -1,6 +1,7 @@ #ifndef _ANDROID_GRAPHICS_GRAPHICS_JNI_H_ #define _ANDROID_GRAPHICS_GRAPHICS_JNI_H_ +#include "Bitmap.h" #include "SkBitmap.h" #include "SkDevice.h" #include "SkPixelRef.h" @@ -49,7 +50,7 @@ public: static void point_to_jpointf(const SkPoint& point, JNIEnv*, jobject jpointf); static android::Canvas* getNativeCanvas(JNIEnv*, jobject canvas); - static SkBitmap* getSkBitmapDeprecated(JNIEnv*, jobject bitmap); + static android::Bitmap* getBitmap(JNIEnv*, jobject bitmap); static void getSkBitmap(JNIEnv*, jobject bitmap, SkBitmap* outBitmap); static SkPixelRef* getSkPixelRef(JNIEnv*, jobject bitmap); static SkRegion* getNativeRegion(JNIEnv*, jobject region); @@ -71,22 +72,18 @@ public: */ static SkColorType getNativeBitmapColorType(JNIEnv*, jobject jconfig); - /** Create a java Bitmap object given the native bitmap (required) and optional - storage array (may be null). - bitmap's SkAlphaType must already be in sync with bitmapCreateFlags. + /* + * Create a java Bitmap object given the native bitmap + * bitmap's SkAlphaType must already be in sync with bitmapCreateFlags. */ - static jobject createBitmap(JNIEnv* env, SkBitmap* bitmap, jbyteArray buffer, - int bitmapCreateFlags, jbyteArray ninePatch, jobject ninePatchInsets, int density = -1); - - static jobject createBitmap(JNIEnv* env, SkBitmap* bitmap, int bitmapCreateFlags, - jbyteArray ninePatch, int density = -1) { - return createBitmap(env, bitmap, NULL, bitmapCreateFlags, ninePatch, NULL, density); - } + static jobject createBitmap(JNIEnv* env, android::Bitmap* bitmap, + int bitmapCreateFlags, jbyteArray ninePatchChunk = NULL, + jobject ninePatchInsets = NULL, int density = -1); /** Reinitialize a bitmap. bitmap must already have its SkAlphaType set in sync with isPremultiplied */ - static void reinitBitmap(JNIEnv* env, jobject javaBitmap, SkBitmap* bitmap, + static void reinitBitmap(JNIEnv* env, jobject javaBitmap, const SkImageInfo& info, bool isPremultiplied); static int getBitmapAllocationByteCount(JNIEnv* env, jobject javaBitmap); @@ -95,7 +92,7 @@ public: static jobject createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap); - static jbyteArray allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap, + static android::Bitmap* allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap, SkColorTable* ctable); /** @@ -113,30 +110,6 @@ public: static bool SetPixels(JNIEnv* env, jintArray colors, int srcOffset, int srcStride, int x, int y, int width, int height, const SkBitmap& dstBitmap); - - static jbyteArray getBitmapStorageObj(SkPixelRef *pixref); -}; - -class AndroidPixelRef : public SkMallocPixelRef { -public: - AndroidPixelRef(JNIEnv* env, const SkImageInfo& info, void* storage, size_t rowBytes, - jbyteArray storageObj, SkColorTable* ctable); - - /** - * Creates an AndroidPixelRef that wraps (and refs) another to reuse/share - * the same storage and java byte array refcounting, yet have a different - * color table. - */ - AndroidPixelRef(AndroidPixelRef& wrappedPixelRef, const SkImageInfo& info, - size_t rowBytes, SkColorTable* ctable); - - virtual ~AndroidPixelRef(); - -private: - AndroidPixelRef* const fWrappedPixelRef; // if set, delegate memory management calls to this - - JavaVM* fVM; - jbyteArray fStorageObj; // The Java byte[] object used as the bitmap backing store }; /** Allocator which allocates the backing buffer in the Java heap. @@ -147,30 +120,22 @@ private: class JavaPixelAllocator : public SkBitmap::Allocator { public: JavaPixelAllocator(JNIEnv* env); - // overrides - virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable); + ~JavaPixelAllocator(); - /** Return the Java array object created for the last allocation. - * This returns a local JNI reference which the caller is responsible - * for storing appropriately (usually by passing it to the Bitmap - * constructor). - */ - jbyteArray getStorageObj() { return fStorageObj; } + virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) override; - /** Same as getStorageObj(), but also resets the allocator so that it - * can allocate again. + /** + * Fetches the backing allocation object. Must be called! */ - jbyteArray getStorageObjAndReset() { - jbyteArray result = fStorageObj; - fStorageObj = NULL; - fAllocCount = 0; + android::Bitmap* getStorageObjAndReset() { + android::Bitmap* result = mStorage; + mStorage = NULL; return result; }; private: - JavaVM* fVM; - jbyteArray fStorageObj; - int fAllocCount; + JavaVM* mJavaVM; + android::Bitmap* mStorage = nullptr; }; enum JNIAccess { diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp index 6afb226..8b65fd1 100644 --- a/core/jni/android_media_AudioRecord.cpp +++ b/core/jni/android_media_AudioRecord.cpp @@ -252,6 +252,7 @@ android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this, sessionId, AudioRecord::TRANSFER_DEFAULT, flags, + -1, -1, // default uid, pid paa); if (status != NO_ERROR) { diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index daf5a61..db495dd 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -999,6 +999,13 @@ static jint android_content_AssetManager_loadThemeAttributeValue( return block >= 0 ? copyValue(env, outValue, &res, value, ref, block, typeSpecFlags) : block; } +static jint android_content_AssetManager_getThemeChangingConfigurations(JNIEnv* env, jobject clazz, + jlong themeHandle) +{ + ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle); + return theme->getChangingConfigurations(); +} + static void android_content_AssetManager_dumpTheme(JNIEnv* env, jobject clazz, jlong themeHandle, jint pri, jstring tag, jstring prefix) @@ -2103,6 +2110,8 @@ static JNINativeMethod gAssetManagerMethods[] = { (void*) android_content_AssetManager_copyTheme }, { "loadThemeAttributeValue", "(JILandroid/util/TypedValue;Z)I", (void*) android_content_AssetManager_loadThemeAttributeValue }, + { "getThemeChangingConfigurations", "(J)I", + (void*) android_content_AssetManager_getThemeChangingConfigurations }, { "dumpTheme", "(JILjava/lang/String;Ljava/lang/String;)V", (void*) android_content_AssetManager_dumpTheme }, { "applyStyle","(JIIJ[I[I[I)Z", diff --git a/core/jni/android_view_DisplayListCanvas.cpp b/core/jni/android_view_DisplayListCanvas.cpp index 39449b0..bb8ef83 100644 --- a/core/jni/android_view_DisplayListCanvas.cpp +++ b/core/jni/android_view_DisplayListCanvas.cpp @@ -86,24 +86,6 @@ static void android_view_DisplayListCanvas_finish(JNIEnv* env, jobject clazz, renderer->finish(); } -static void android_view_DisplayListCanvas_setProperty(JNIEnv* env, - jobject clazz, jstring name, jstring value) { - if (!Caches::hasInstance()) { - ALOGW("can't set property, no Caches instance"); - return; - } - - if (name == NULL || value == NULL) { - ALOGW("can't set prop, null passed"); - } - - const char* nameCharArray = env->GetStringUTFChars(name, NULL); - const char* valueCharArray = env->GetStringUTFChars(value, NULL); - Caches::getInstance().setTempProperty(nameCharArray, valueCharArray); - env->ReleaseStringUTFChars(name, nameCharArray); - env->ReleaseStringUTFChars(name, valueCharArray); -} - // ---------------------------------------------------------------------------- // Functor // ---------------------------------------------------------------------------- @@ -268,8 +250,6 @@ static JNINativeMethod gMethods[] = { { "nPrepare", "(J)V", (void*) android_view_DisplayListCanvas_prepare }, { "nPrepareDirty", "(JIIII)V", (void*) android_view_DisplayListCanvas_prepareDirty }, { "nFinish", "(J)V", (void*) android_view_DisplayListCanvas_finish }, - { "nSetProperty", "(Ljava/lang/String;Ljava/lang/String;)V", - (void*) android_view_DisplayListCanvas_setProperty }, { "nCallDrawGLFunction", "(JJ)V", (void*) android_view_DisplayListCanvas_callDrawGLFunction }, diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index a8355c2..1965cd3 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -23,6 +23,7 @@ #include "android_os_Parcel.h" #include "android_util_Binder.h" +#include "android/graphics/Bitmap.h" #include "android/graphics/GraphicsJNI.h" #include "android/graphics/Region.h" @@ -168,22 +169,19 @@ static jobject nativeScreenshotBitmap(JNIEnv* env, jclass clazz, } } - const ssize_t rowBytes = + const size_t rowBytes = screenshot->getStride() * android::bytesPerPixel(screenshot->getFormat()); - SkBitmap* bitmap = new SkBitmap(); - bitmap->setInfo(screenshotInfo, (size_t)rowBytes); - if (screenshotInfo.fWidth > 0 && screenshotInfo.fHeight > 0) { - // takes ownership of ScreenshotClient - SkMallocPixelRef* pixels = SkMallocPixelRef::NewWithProc(screenshotInfo, - (size_t) rowBytes, NULL, (void*) screenshot->getPixels(), &DeleteScreenshot, - (void*) (screenshot.get())); - screenshot.detach(); - pixels->setImmutable(); - bitmap->setPixelRef(pixels)->unref(); - bitmap->lockPixels(); + if (!screenshotInfo.fWidth || !screenshotInfo.fHeight) { + return NULL; } + Bitmap* bitmap = new Bitmap( + (void*) screenshot->getPixels(), (void*) screenshot.get(), DeleteScreenshot, + screenshotInfo, rowBytes, nullptr); + screenshot.detach(); + bitmap->pixelRef()->setImmutable(); + return GraphicsJNI::createBitmap(env, bitmap, GraphicsJNI::kBitmapCreateFlag_Premultiplied, NULL); } diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp index 4ccbb41..5d5465b 100644 --- a/core/jni/android_view_ThreadedRenderer.cpp +++ b/core/jni/android_view_ThreadedRenderer.cpp @@ -388,6 +388,15 @@ static void android_view_ThreadedRenderer_trimMemory(JNIEnv* env, jobject clazz, RenderProxy::trimMemory(level); } +static void android_view_ThreadedRenderer_overrideProperty(JNIEnv* env, jobject clazz, + jstring name, jstring value) { + const char* nameCharArray = env->GetStringUTFChars(name, NULL); + const char* valueCharArray = env->GetStringUTFChars(value, NULL); + RenderProxy::overrideProperty(nameCharArray, valueCharArray); + env->ReleaseStringUTFChars(name, nameCharArray); + env->ReleaseStringUTFChars(name, valueCharArray); +} + static void android_view_ThreadedRenderer_fence(JNIEnv* env, jobject clazz, jlong proxyPtr) { RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); @@ -466,6 +475,7 @@ static JNINativeMethod gMethods[] = { { "nDetachSurfaceTexture", "(JJ)V", (void*) android_view_ThreadedRenderer_detachSurfaceTexture }, { "nDestroyHardwareResources", "(J)V", (void*) android_view_ThreadedRenderer_destroyHardwareResources }, { "nTrimMemory", "(I)V", (void*) android_view_ThreadedRenderer_trimMemory }, + { "nOverrideProperty", "(Ljava/lang/String;Ljava/lang/String;)V", (void*) android_view_ThreadedRenderer_overrideProperty }, { "nFence", "(J)V", (void*) android_view_ThreadedRenderer_fence }, { "nStopDrawing", "(J)V", (void*) android_view_ThreadedRenderer_stopDrawing }, { "nNotifyFramePending", "(J)V", (void*) android_view_ThreadedRenderer_notifyFramePending }, diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 942e6a6..a162b4a 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2433,6 +2433,12 @@ <permission android:name="android.permission.QUERY_DO_NOT_ASK_CREDENTIALS_ON_BOOT" android:protectionLevel="signature" /> + <!-- @SystemApi Allows applications to kill UIDs. + <p>Not for use by third-party applications. + @hide --> + <permission android:name="android.permission.KILL_UID" + android:protectionLevel="signature" /> + <!-- The system process is explicitly the only one allowed to launch the confirmation UI for full backup/restore --> <uses-permission android:name="android.permission.CONFIRM_FULL_BACKUP"/> diff --git a/core/res/res/values-mcc311-mnc480/config.xml b/core/res/res/values-mcc311-mnc480/config.xml index 39ea2bf..a986b75 100755 --- a/core/res/res/values-mcc311-mnc480/config.xml +++ b/core/res/res/values-mcc311-mnc480/config.xml @@ -46,7 +46,7 @@ <!-- Flag specifying whether VT should be available for carrier: independent of carrier provisioning. If false: hard disabled. If true: then depends on carrier provisioning, availability etc --> - <bool name="config_carrier_vt_available">true</bool> + <bool name="config_carrier_vt_available">false</bool> <!-- Flag specifying whether VoLTE availability is based on provisioning --> <bool name="config_carrier_volte_provisioned">true</bool> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index f36d448..55b32e1 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -255,6 +255,8 @@ <string-array name="wfcOperatorErrorNotificationMessages" /> <!-- Template for showing cellular network operator name while WFC is active --> <string name="wfcSpnFormat">%s</string> + <!-- Template for showing operator name for data connection while WFC is active --> + <string name="wfcDataSpnFormat">%s</string> <!-- WFC, summary for Disabled --> <string name="wifi_calling_off_summary">Off</string> <!-- WFC, summary for Wi-Fi Preferred --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index f8d276f..a57b3b8 100755 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -773,6 +773,7 @@ <java-symbol type="array" name="wfcOperatorErrorAlertMessages" /> <java-symbol type="array" name="wfcOperatorErrorNotificationMessages" /> <java-symbol type="string" name="wfcSpnFormat" /> + <java-symbol type="string" name="wfcDataSpnFormat" /> <java-symbol type="string" name="wifi_calling_off_summary" /> <java-symbol type="string" name="wfc_mode_wifi_preferred_summary" /> <java-symbol type="string" name="wfc_mode_cellular_preferred_summary" /> diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java index be5c52b..c850b07 100644 --- a/graphics/java/android/graphics/Bitmap.java +++ b/graphics/java/android/graphics/Bitmap.java @@ -41,13 +41,13 @@ public final class Bitmap implements Parcelable { */ public static final int DENSITY_NONE = 0; - private final long mSkBitmapPtr; - /** * Backing buffer for the Bitmap. */ private byte[] mBuffer; + // Convenience for JNI access + private final long mNativePtr; private final BitmapFinalizer mFinalizer; private final boolean mIsMutable; @@ -115,17 +115,16 @@ public final class Bitmap implements Parcelable { mRequestPremultiplied = requestPremultiplied; mBuffer = buffer; - // we delete this in our finalizer - mSkBitmapPtr = nativeBitmap; - mNinePatchChunk = ninePatchChunk; mNinePatchInsets = ninePatchInsets; if (density >= 0) { mDensity = density; } - int nativeAllocationByteCount = buffer == null ? getByteCount() : 0; - mFinalizer = new BitmapFinalizer(nativeBitmap, nativeAllocationByteCount); + mNativePtr = nativeBitmap; + mFinalizer = new BitmapFinalizer(nativeBitmap); + int nativeAllocationByteCount = (buffer == null ? getByteCount() : 0); + mFinalizer.setNativeAllocationByteCount(nativeAllocationByteCount); } /** @@ -223,8 +222,8 @@ public final class Bitmap implements Parcelable { throw new IllegalStateException("native-backed bitmaps may not be reconfigured"); } - nativeReconfigure(mSkBitmapPtr, width, height, config.nativeInt, mBuffer.length, - mRequestPremultiplied); + nativeReconfigure(mFinalizer.mNativeBitmap, width, height, config.nativeInt, + mBuffer.length, mRequestPremultiplied); mWidth = width; mHeight = height; } @@ -301,7 +300,7 @@ public final class Bitmap implements Parcelable { */ public void recycle() { if (!mRecycled && mFinalizer.mNativeBitmap != 0) { - if (nativeRecycle(mSkBitmapPtr)) { + if (nativeRecycle(mFinalizer.mNativeBitmap)) { // return value indicates whether native pixel object was actually recycled. // false indicates that it is still in use at the native level and these // objects should not be collected now. They will be collected later when the @@ -331,7 +330,7 @@ public final class Bitmap implements Parcelable { * @return The current generation ID for this bitmap. */ public int getGenerationId() { - return nativeGenerationId(mSkBitmapPtr); + return nativeGenerationId(mFinalizer.mNativeBitmap); } /** @@ -487,7 +486,7 @@ public final class Bitmap implements Parcelable { throw new RuntimeException("Buffer not large enough for pixels"); } - nativeCopyPixelsToBuffer(mSkBitmapPtr, dst); + nativeCopyPixelsToBuffer(mFinalizer.mNativeBitmap, dst); // now update the buffer's position int position = dst.position(); @@ -527,7 +526,7 @@ public final class Bitmap implements Parcelable { throw new RuntimeException("Buffer not large enough for pixels"); } - nativeCopyPixelsFromBuffer(mSkBitmapPtr, src); + nativeCopyPixelsFromBuffer(mFinalizer.mNativeBitmap, src); // now update the buffer's position int position = src.position(); @@ -549,7 +548,7 @@ public final class Bitmap implements Parcelable { */ public Bitmap copy(Config config, boolean isMutable) { checkRecycled("Can't copy a recycled bitmap"); - Bitmap b = nativeCopy(mSkBitmapPtr, config.nativeInt, isMutable); + Bitmap b = nativeCopy(mFinalizer.mNativeBitmap, config.nativeInt, isMutable); if (b != null) { b.setPremultiplied(mRequestPremultiplied); b.mDensity = mDensity; @@ -810,7 +809,7 @@ public final class Bitmap implements Parcelable { } bm.setHasAlpha(hasAlpha); if (config == Config.ARGB_8888 && !hasAlpha) { - nativeErase(bm.mSkBitmapPtr, 0xff000000); + nativeErase(bm.mFinalizer.mNativeBitmap, 0xff000000); } // No need to initialize the bitmap to zeroes with other configs; // it is backed by a VM byte array which is by definition preinitialized @@ -1000,8 +999,8 @@ public final class Bitmap implements Parcelable { throw new IllegalArgumentException("quality must be 0..100"); } Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "Bitmap.compress"); - boolean result = nativeCompress(mSkBitmapPtr, format.nativeInt, quality, - stream, new byte[WORKING_COMPRESS_STORAGE]); + boolean result = nativeCompress(mFinalizer.mNativeBitmap, format.nativeInt, + quality, stream, new byte[WORKING_COMPRESS_STORAGE]); Trace.traceEnd(Trace.TRACE_TAG_RESOURCES); return result; } @@ -1041,7 +1040,7 @@ public final class Bitmap implements Parcelable { * @see BitmapFactory.Options#inPremultiplied */ public final boolean isPremultiplied() { - return nativeIsPremultiplied(mSkBitmapPtr); + return nativeIsPremultiplied(mFinalizer.mNativeBitmap); } /** @@ -1066,7 +1065,7 @@ public final class Bitmap implements Parcelable { */ public final void setPremultiplied(boolean premultiplied) { mRequestPremultiplied = premultiplied; - nativeSetPremultiplied(mSkBitmapPtr, premultiplied); + nativeSetPremultiplied(mFinalizer.mNativeBitmap, premultiplied); } /** Returns the bitmap's width */ @@ -1158,7 +1157,7 @@ public final class Bitmap implements Parcelable { * @return number of bytes between rows of the native bitmap pixels. */ public final int getRowBytes() { - return nativeRowBytes(mSkBitmapPtr); + return nativeRowBytes(mFinalizer.mNativeBitmap); } /** @@ -1201,7 +1200,7 @@ public final class Bitmap implements Parcelable { * that config, otherwise return null. */ public final Config getConfig() { - return Config.nativeToConfig(nativeConfig(mSkBitmapPtr)); + return Config.nativeToConfig(nativeConfig(mFinalizer.mNativeBitmap)); } /** Returns true if the bitmap's config supports per-pixel alpha, and @@ -1213,7 +1212,7 @@ public final class Bitmap implements Parcelable { * it will return true by default. */ public final boolean hasAlpha() { - return nativeHasAlpha(mSkBitmapPtr); + return nativeHasAlpha(mFinalizer.mNativeBitmap); } /** @@ -1227,7 +1226,7 @@ public final class Bitmap implements Parcelable { * non-opaque per-pixel alpha values. */ public void setHasAlpha(boolean hasAlpha) { - nativeSetHasAlpha(mSkBitmapPtr, hasAlpha, mRequestPremultiplied); + nativeSetHasAlpha(mFinalizer.mNativeBitmap, hasAlpha, mRequestPremultiplied); } /** @@ -1248,7 +1247,7 @@ public final class Bitmap implements Parcelable { * @see #setHasMipMap(boolean) */ public final boolean hasMipMap() { - return nativeHasMipMap(mSkBitmapPtr); + return nativeHasMipMap(mFinalizer.mNativeBitmap); } /** @@ -1272,7 +1271,7 @@ public final class Bitmap implements Parcelable { * @see #hasMipMap() */ public final void setHasMipMap(boolean hasMipMap) { - nativeSetHasMipMap(mSkBitmapPtr, hasMipMap); + nativeSetHasMipMap(mFinalizer.mNativeBitmap, hasMipMap); } /** @@ -1285,7 +1284,7 @@ public final class Bitmap implements Parcelable { if (!isMutable()) { throw new IllegalStateException("cannot erase immutable bitmaps"); } - nativeErase(mSkBitmapPtr, c); + nativeErase(mFinalizer.mNativeBitmap, c); } /** @@ -1302,7 +1301,7 @@ public final class Bitmap implements Parcelable { public int getPixel(int x, int y) { checkRecycled("Can't call getPixel() on a recycled bitmap"); checkPixelAccess(x, y); - return nativeGetPixel(mSkBitmapPtr, x, y); + return nativeGetPixel(mFinalizer.mNativeBitmap, x, y); } /** @@ -1335,7 +1334,7 @@ public final class Bitmap implements Parcelable { return; // nothing to do } checkPixelsAccess(x, y, width, height, offset, stride, pixels); - nativeGetPixels(mSkBitmapPtr, pixels, offset, stride, + nativeGetPixels(mFinalizer.mNativeBitmap, pixels, offset, stride, x, y, width, height); } @@ -1416,7 +1415,7 @@ public final class Bitmap implements Parcelable { throw new IllegalStateException(); } checkPixelAccess(x, y); - nativeSetPixel(mSkBitmapPtr, x, y, color); + nativeSetPixel(mFinalizer.mNativeBitmap, x, y, color); } /** @@ -1452,7 +1451,7 @@ public final class Bitmap implements Parcelable { return; // nothing to do } checkPixelsAccess(x, y, width, height, offset, stride, pixels); - nativeSetPixels(mSkBitmapPtr, pixels, offset, stride, + nativeSetPixels(mFinalizer.mNativeBitmap, pixels, offset, stride, x, y, width, height); } @@ -1490,7 +1489,7 @@ public final class Bitmap implements Parcelable { */ public void writeToParcel(Parcel p, int flags) { checkRecycled("Can't parcel a recycled bitmap"); - if (!nativeWriteToParcel(mSkBitmapPtr, mIsMutable, mDensity, p)) { + if (!nativeWriteToParcel(mFinalizer.mNativeBitmap, mIsMutable, mDensity, p)) { throw new RuntimeException("native writeToParcel failed"); } } @@ -1536,7 +1535,7 @@ public final class Bitmap implements Parcelable { public Bitmap extractAlpha(Paint paint, int[] offsetXY) { checkRecycled("Can't extractAlpha on a recycled bitmap"); long nativePaint = paint != null ? paint.getNativeInstance() : 0; - Bitmap bm = nativeExtractAlpha(mSkBitmapPtr, nativePaint, offsetXY); + Bitmap bm = nativeExtractAlpha(mFinalizer.mNativeBitmap, nativePaint, offsetXY); if (bm == null) { throw new RuntimeException("Failed to extractAlpha on Bitmap"); } @@ -1550,7 +1549,8 @@ public final class Bitmap implements Parcelable { * If other is null, return false. */ public boolean sameAs(Bitmap other) { - return this == other || (other != null && nativeSameAs(mSkBitmapPtr, other.mSkBitmapPtr)); + return this == other || (other != null + && nativeSameAs(mFinalizer.mNativeBitmap, other.mFinalizer.mNativeBitmap)); } /** @@ -1565,7 +1565,9 @@ public final class Bitmap implements Parcelable { * and therefore is harmless. */ public void prepareToDraw() { - nativePrepareToDraw(mSkBitmapPtr); + // TODO: Consider having this start an async upload? + // With inPurgeable no-op'd there's currently no use for this + // method, but it could have interesting future uses. } /** @@ -1574,7 +1576,7 @@ public final class Bitmap implements Parcelable { * @hide * */ public final long refSkPixelRef() { - return nativeRefPixelRef(mSkBitmapPtr); + return nativeRefPixelRef(mNativePtr); } private static class BitmapFinalizer { @@ -1582,12 +1584,17 @@ public final class Bitmap implements Parcelable { // Native memory allocated for the duration of the Bitmap, // if pixel data allocated into native memory, instead of java byte[] - private final int mNativeAllocationByteCount; + private int mNativeAllocationByteCount; - BitmapFinalizer(long nativeBitmap, int nativeAllocationByteCount) { + BitmapFinalizer(long nativeBitmap) { mNativeBitmap = nativeBitmap; - mNativeAllocationByteCount = nativeAllocationByteCount; + } + public void setNativeAllocationByteCount(int nativeByteCount) { + if (mNativeAllocationByteCount != 0) { + VMRuntime.getRuntime().registerNativeFree(mNativeAllocationByteCount); + } + mNativeAllocationByteCount = nativeByteCount; if (mNativeAllocationByteCount != 0) { VMRuntime.getRuntime().registerNativeAllocation(mNativeAllocationByteCount); } @@ -1600,9 +1607,7 @@ public final class Bitmap implements Parcelable { } catch (Throwable t) { // Ignore } finally { - if (mNativeAllocationByteCount != 0) { - VMRuntime.getRuntime().registerNativeFree(mNativeAllocationByteCount); - } + setNativeAllocationByteCount(0); nativeDestructor(mNativeBitmap); mNativeBitmap = 0; } @@ -1654,7 +1659,6 @@ public final class Bitmap implements Parcelable { long nativePaint, int[] offsetXY); - private static native void nativePrepareToDraw(long nativeBitmap); private static native boolean nativeHasAlpha(long nativeBitmap); private static native boolean nativeIsPremultiplied(long nativeBitmap); private static native void nativeSetPremultiplied(long nativeBitmap, diff --git a/include/androidfw/ResourceTypes.h b/include/androidfw/ResourceTypes.h index df278c8..587e7fa 100644 --- a/include/androidfw/ResourceTypes.h +++ b/include/androidfw/ResourceTypes.h @@ -1662,6 +1662,12 @@ public: uint32_t* inoutTypeSpecFlags = NULL, ResTable_config* inoutConfig = NULL) const; + /** + * Returns a bit mask of configuration changes that will impact this + * theme (and thus require completely reloading it). + */ + uint32_t getChangingConfigurations() const; + void dumpToLog() const; private: @@ -1688,6 +1694,7 @@ public: const ResTable& mTable; package_info* mPackages[Res_MAXPACKAGE]; + uint32_t mTypeSpecFlags; }; void setParameters(const ResTable_config* params); diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index 04ebe70..19a5beb 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -3147,6 +3147,7 @@ struct ResTable::bag_set ResTable::Theme::Theme(const ResTable& table) : mTable(table) + , mTypeSpecFlags(0) { memset(mPackages, 0, sizeof(mPackages)); } @@ -3205,6 +3206,8 @@ status_t ResTable::Theme::applyStyle(uint32_t resID, bool force) return N; } + mTypeSpecFlags |= bagTypeSpecFlags; + uint32_t curPackage = 0xffffffff; ssize_t curPackageIndex = 0; package_info* curPI = NULL; @@ -3323,6 +3326,8 @@ status_t ResTable::Theme::setTo(const Theme& other) } } + mTypeSpecFlags = other.mTypeSpecFlags; + if (kDebugTableTheme) { ALOGI("Final theme:"); dumpToLog(); @@ -3417,6 +3422,11 @@ ssize_t ResTable::Theme::resolveAttributeReference(Res_value* inOutValue, inoutTypeSpecFlags, inoutConfig); } +uint32_t ResTable::Theme::getChangingConfigurations() const +{ + return mTypeSpecFlags; +} + void ResTable::Theme::dumpToLog() const { ALOGI("Theme %p:\n", this); diff --git a/libs/hwui/Android.common.mk b/libs/hwui/Android.common.mk index 836f868..8a4e609 100644 --- a/libs/hwui/Android.common.mk +++ b/libs/hwui/Android.common.mk @@ -63,6 +63,7 @@ LOCAL_SRC_FILES := \ PixelBuffer.cpp \ Program.cpp \ ProgramCache.cpp \ + Properties.cpp \ RenderBufferCache.cpp \ RenderNode.cpp \ RenderProperties.cpp \ diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp index fd5a2ce..f75d6a0 100644 --- a/libs/hwui/Caches.cpp +++ b/libs/hwui/Caches.cpp @@ -57,14 +57,8 @@ Caches::Caches(RenderState& renderState) init(); initFont(); initConstraints(); - initProperties(); initStaticProperties(); initExtensions(); - initTempProperties(); - - mDebugLevel = readDebugLevel(); - ALOGD_IF(mDebugLevel != kDebugDisabled, - "Enabling debug mode %d", mDebugLevel); } bool Caches::init() { @@ -77,10 +71,6 @@ bool Caches::init() { mFunctorsCount = 0; - debugLayersUpdates = false; - debugOverdraw = false; - debugStencilClip = kStencilHide; - patchCache.init(); mInitialized = true; @@ -124,66 +114,6 @@ void Caches::initStaticProperties() { } } -bool Caches::initProperties() { - bool prevDebugLayersUpdates = debugLayersUpdates; - bool prevDebugOverdraw = debugOverdraw; - StencilClipDebug prevDebugStencilClip = debugStencilClip; - - char property[PROPERTY_VALUE_MAX]; - if (property_get(PROPERTY_DEBUG_LAYERS_UPDATES, property, nullptr) > 0) { - INIT_LOGD(" Layers updates debug enabled: %s", property); - debugLayersUpdates = !strcmp(property, "true"); - } else { - debugLayersUpdates = false; - } - - debugOverdraw = false; - if (property_get(PROPERTY_DEBUG_OVERDRAW, property, nullptr) > 0) { - INIT_LOGD(" Overdraw debug enabled: %s", property); - if (!strcmp(property, "show")) { - debugOverdraw = true; - mOverdrawDebugColorSet = kColorSet_Default; - } else if (!strcmp(property, "show_deuteranomaly")) { - debugOverdraw = true; - mOverdrawDebugColorSet = kColorSet_Deuteranomaly; - } - } - - // See Properties.h for valid values - if (property_get(PROPERTY_DEBUG_STENCIL_CLIP, property, nullptr) > 0) { - INIT_LOGD(" Stencil clip debug enabled: %s", property); - if (!strcmp(property, "hide")) { - debugStencilClip = kStencilHide; - } else if (!strcmp(property, "highlight")) { - debugStencilClip = kStencilShowHighlight; - } else if (!strcmp(property, "region")) { - debugStencilClip = kStencilShowRegion; - } - } else { - debugStencilClip = kStencilHide; - } - - if (property_get(PROPERTY_DISABLE_DRAW_DEFER, property, "false")) { - drawDeferDisabled = !strcasecmp(property, "true"); - INIT_LOGD(" Draw defer %s", drawDeferDisabled ? "disabled" : "enabled"); - } else { - drawDeferDisabled = false; - INIT_LOGD(" Draw defer enabled"); - } - - if (property_get(PROPERTY_DISABLE_DRAW_REORDER, property, "false")) { - drawReorderDisabled = !strcasecmp(property, "true"); - INIT_LOGD(" Draw reorder %s", drawReorderDisabled ? "disabled" : "enabled"); - } else { - drawReorderDisabled = false; - INIT_LOGD(" Draw reorder enabled"); - } - - return (prevDebugLayersUpdates != debugLayersUpdates) - || (prevDebugOverdraw != debugOverdraw) - || (prevDebugStencilClip != debugStencilClip); -} - void Caches::terminate() { if (!mInitialized) return; mRegionMesh.release(); @@ -231,7 +161,9 @@ uint32_t Caches::getOverdrawColor(uint32_t amount) const { }; if (amount < 1) amount = 1; if (amount > 4) amount = 4; - return sOverdrawColors[mOverdrawDebugColorSet][amount - 1]; + + int overdrawColorIndex = static_cast<int>(Properties::overdrawColorSet); + return sOverdrawColors[overdrawColorIndex][amount - 1]; } void Caches::dumpMemoryUsage() { @@ -351,13 +283,13 @@ void Caches::flush(FlushMode mode) { /////////////////////////////////////////////////////////////////////////////// void Caches::startTiling(GLuint x, GLuint y, GLuint width, GLuint height, bool discard) { - if (mExtensions.hasTiledRendering() && !debugOverdraw) { + if (mExtensions.hasTiledRendering() && !Properties::debugOverdraw) { glStartTilingQCOM(x, y, width, height, (discard ? GL_NONE : GL_COLOR_BUFFER_BIT0_QCOM)); } } void Caches::endTiling() { - if (mExtensions.hasTiledRendering() && !debugOverdraw) { + if (mExtensions.hasTiledRendering() && !Properties::debugOverdraw) { glEndTilingQCOM(GL_COLOR_BUFFER_BIT0_QCOM); } } @@ -395,44 +327,5 @@ TextureVertex* Caches::getRegionMesh() { // Temporary Properties /////////////////////////////////////////////////////////////////////////////// -void Caches::initTempProperties() { - propertyLightRadius = -1.0f; - propertyLightPosY = -1.0f; - propertyLightPosZ = -1.0f; - propertyAmbientRatio = -1.0f; - propertyAmbientShadowStrength = -1; - propertySpotShadowStrength = -1; -} - -void Caches::setTempProperty(const char* name, const char* value) { - ALOGD("setting property %s to %s", name, value); - if (!strcmp(name, "ambientRatio")) { - propertyAmbientRatio = fmin(fmax(atof(value), 0.0), 10.0); - ALOGD("ambientRatio = %.2f", propertyAmbientRatio); - return; - } else if (!strcmp(name, "lightRadius")) { - propertyLightRadius = fmin(fmax(atof(value), 0.0), 3000.0); - ALOGD("lightRadius = %.2f", propertyLightRadius); - return; - } else if (!strcmp(name, "lightPosY")) { - propertyLightPosY = fmin(fmax(atof(value), 0.0), 3000.0); - ALOGD("lightPos Y = %.2f", propertyLightPosY); - return; - } else if (!strcmp(name, "lightPosZ")) { - propertyLightPosZ = fmin(fmax(atof(value), 0.0), 3000.0); - ALOGD("lightPos Z = %.2f", propertyLightPosZ); - return; - } else if (!strcmp(name, "ambientShadowStrength")) { - propertyAmbientShadowStrength = atoi(value); - ALOGD("ambient shadow strength = 0x%x out of 0xff", propertyAmbientShadowStrength); - return; - } else if (!strcmp(name, "spotShadowStrength")) { - propertySpotShadowStrength = atoi(value); - ALOGD("spot shadow strength = 0x%x out of 0xff", propertySpotShadowStrength); - return; - } - ALOGD(" failed"); -} - }; // namespace uirenderer }; // namespace android diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h index 8aea8ff..804f609 100644 --- a/libs/hwui/Caches.h +++ b/libs/hwui/Caches.h @@ -80,7 +80,7 @@ public: } static bool hasInstance() { - return sInstance != 0; + return sInstance != nullptr; } private: Caches(RenderState& renderState); @@ -99,11 +99,6 @@ public: bool init(); /** - * Initialize global system properties. - */ - bool initProperties(); - - /** * Flush the cache. * * @param mode Indicates how much of the cache should be flushed @@ -117,14 +112,6 @@ public: void terminate(); /** - * Indicates whether the renderer is in debug mode. - * This debug mode provides limited information to app developers. - */ - DebugLevel getDebugLevel() const { - return mDebugLevel; - } - - /** * Returns a non-premultiplied ARGB color for the specified * amount of overdraw (1 for 1x, 2 for 2x, etc.) */ @@ -162,22 +149,9 @@ public: void registerFunctors(uint32_t functorCount); void unregisterFunctors(uint32_t functorCount); - bool drawDeferDisabled; - bool drawReorderDisabled; - // Misc GLint maxTextureSize; - // Debugging - bool debugLayersUpdates; - bool debugOverdraw; - - enum StencilClipDebug { - kStencilHide, - kStencilShowHighlight, - kStencilShowRegion - }; - StencilClipDebug debugStencilClip; private: // Declared before gradientCache and programCache which need this to initialize. // TODO: cleanup / move elsewhere @@ -207,17 +181,6 @@ public: PFNGLPUSHGROUPMARKEREXTPROC startMark; PFNGLPOPGROUPMARKEREXTPROC endMark; - // TEMPORARY properties - void initTempProperties(); - void setTempProperty(const char* name, const char* value); - - float propertyLightRadius; - float propertyLightPosY; - float propertyLightPosZ; - float propertyAmbientRatio; - int propertyAmbientShadowStrength; - int propertySpotShadowStrength; - void setProgram(const ProgramDescription& description); void setProgram(Program* program); @@ -227,10 +190,6 @@ public: TextureState& textureState() { return *mTextureState; } private: - enum OverdrawColorSet { - kColorSet_Default = 0, - kColorSet_Deuteranomaly - }; void initFont(); void initExtensions(); @@ -249,13 +208,10 @@ private: mutable Mutex mGarbageLock; Vector<Layer*> mLayerGarbage; - DebugLevel mDebugLevel; bool mInitialized; uint32_t mFunctorsCount; - OverdrawColorSet mOverdrawDebugColorSet; - // TODO: move below to RenderState PixelBufferState* mPixelBufferState = nullptr; TextureState* mTextureState = nullptr; diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp index dd6af03..6fcf958 100644 --- a/libs/hwui/DeferredDisplayList.cpp +++ b/libs/hwui/DeferredDisplayList.cpp @@ -28,6 +28,7 @@ #include "DeferredDisplayList.h" #include "DisplayListOp.h" #include "OpenGLRenderer.h" +#include "Properties.h" #include "utils/MathUtils.h" #if DEBUG_DEFER @@ -502,7 +503,7 @@ void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) { resetBatchingState(); } - if (CC_UNLIKELY(renderer.getCaches().drawReorderDisabled)) { + if (CC_UNLIKELY(Properties::drawReorderDisabled)) { // TODO: elegant way to reuse batches? DrawBatch* b = new DrawBatch(deferInfo); b->add(op, state, deferInfo.opaqueOverBounds); diff --git a/libs/hwui/DrawProfiler.cpp b/libs/hwui/DrawProfiler.cpp index ecde5ff..7addef9 100644 --- a/libs/hwui/DrawProfiler.cpp +++ b/libs/hwui/DrawProfiler.cpp @@ -18,12 +18,11 @@ #include <cutils/compiler.h> #include "OpenGLRenderer.h" -#include "Properties.h" #define DEFAULT_MAX_FRAMES 128 -#define RETURN_IF_PROFILING_DISABLED() if (CC_LIKELY(mType == kNone)) return -#define RETURN_IF_DISABLED() if (CC_LIKELY(mType == kNone && !mShowDirtyRegions)) return +#define RETURN_IF_PROFILING_DISABLED() if (CC_LIKELY(mType == ProfileType::None)) return +#define RETURN_IF_DISABLED() if (CC_LIKELY(mType == ProfileType::None && !mShowDirtyRegions)) return #define NANOS_TO_MILLIS_FLOAT(nanos) ((nanos) * 0.000001f) @@ -56,18 +55,7 @@ static int dpToPx(int dp, float density) { return (int) (dp * density + 0.5f); } -DrawProfiler::DrawProfiler() - : mType(kNone) - , mDensity(0) - , mData(nullptr) - , mDataSize(0) - , mCurrentFrame(-1) - , mPreviousTime(0) - , mVerticalUnit(0) - , mHorizontalUnit(0) - , mThresholdStroke(0) - , mShowDirtyRegions(false) - , mFlashToggle(false) { +DrawProfiler::DrawProfiler() { setDensity(1); } @@ -135,7 +123,7 @@ void DrawProfiler::draw(OpenGLRenderer* canvas) { } } - if (mType == kBars) { + if (mType == ProfileType::Bars) { prepareShapes(canvas->getViewportHeight()); drawGraph(canvas); drawCurrentFrame(canvas); @@ -217,32 +205,20 @@ void DrawProfiler::drawThreshold(OpenGLRenderer* canvas) { canvas->drawLines(pts, 4, &paint); } -DrawProfiler::ProfileType DrawProfiler::loadRequestedProfileType() { - ProfileType type = kNone; - char buf[PROPERTY_VALUE_MAX] = {'\0',}; - if (property_get(PROPERTY_PROFILE, buf, "") > 0) { - if (!strcmp(buf, PROPERTY_PROFILE_VISUALIZE_BARS)) { - type = kBars; - } else if (!strcmp(buf, "true")) { - type = kConsole; - } - } - return type; -} - -bool DrawProfiler::loadSystemProperties() { +bool DrawProfiler::consumeProperties() { bool changed = false; - ProfileType newType = loadRequestedProfileType(); + ProfileType newType = Properties::getProfileType(); if (newType != mType) { mType = newType; - if (mType == kNone) { + if (mType == ProfileType::None) { destroyData(); } else { createData(); } changed = true; } - bool showDirty = property_get_bool(PROPERTY_DEBUG_SHOW_DIRTY_REGIONS, false); + + bool showDirty = Properties::showDirtyRegions; if (showDirty != mShowDirtyRegions) { mShowDirtyRegions = showDirty; changed = true; diff --git a/libs/hwui/DrawProfiler.h b/libs/hwui/DrawProfiler.h index de64088..ef6101c 100644 --- a/libs/hwui/DrawProfiler.h +++ b/libs/hwui/DrawProfiler.h @@ -16,9 +16,11 @@ #ifndef DRAWPROFILER_H #define DRAWPROFILER_H -#include <utils/Timers.h> +#include "Properties.h" #include "Rect.h" +#include <utils/Timers.h> + namespace android { namespace uirenderer { @@ -29,7 +31,7 @@ public: DrawProfiler(); ~DrawProfiler(); - bool loadSystemProperties(); + bool consumeProperties(); void setDensity(float density); void startFrame(nsecs_t recordDurationNanos = 0); @@ -43,12 +45,6 @@ public: void dumpData(int fd); private: - enum ProfileType { - kNone, - kConsole, - kBars, - }; - typedef struct { float record; float prepare; @@ -65,20 +61,18 @@ private: void drawCurrentFrame(OpenGLRenderer* canvas); void drawThreshold(OpenGLRenderer* canvas); - ProfileType loadRequestedProfileType(); - - ProfileType mType; - float mDensity; + ProfileType mType = ProfileType::None; + float mDensity = 0; - FrameTimingData* mData; - int mDataSize; + FrameTimingData* mData = nullptr; + int mDataSize = 0; - int mCurrentFrame; - nsecs_t mPreviousTime; + int mCurrentFrame = -1; + nsecs_t mPreviousTime = 0; - int mVerticalUnit; - int mHorizontalUnit; - int mThresholdStroke; + int mVerticalUnit = 0; + int mHorizontalUnit = 0; + int mThresholdStroke = 0; /* * mRects represents an array of rect shapes, divided into NUM_ELEMENTS @@ -87,11 +81,11 @@ private: * OpenGLRenderer:drawRects() that makes up all the FrameTimingData:record * information. */ - float** mRects; + float** mRects = nullptr; - bool mShowDirtyRegions; + bool mShowDirtyRegions = false; SkRect mDirtyRegion; - bool mFlashToggle; + bool mFlashToggle = false; }; } /* namespace uirenderer */ diff --git a/libs/hwui/GlopBuilder.cpp b/libs/hwui/GlopBuilder.cpp index 9ca6bc6..e25f81e 100644 --- a/libs/hwui/GlopBuilder.cpp +++ b/libs/hwui/GlopBuilder.cpp @@ -588,8 +588,8 @@ void GlopBuilder::build() { // Enable debug highlight when what we're about to draw is tested against // the stencil buffer and if stencil highlight debugging is on - mDescription.hasDebugHighlight = !mCaches.debugOverdraw - && mCaches.debugStencilClip == Caches::kStencilShowHighlight + mDescription.hasDebugHighlight = !Properties::debugOverdraw + && Properties::debugStencilClip == StencilClipDebug::ShowHighlight && mRenderState.stencil().isTestEnabled(); // serialize shader info into ShaderData diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index 7fc31b8..09674f7 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -245,7 +245,7 @@ bool OpenGLRenderer::finish() { #if DEBUG_MEMORY_USAGE mCaches.dumpMemoryUsage(); #else - if (mCaches.getDebugLevel() & kDebugMemory) { + if (Properties::debugLevel & kDebugMemory) { mCaches.dumpMemoryUsage(); } #endif @@ -339,7 +339,7 @@ void OpenGLRenderer::debugOverdraw(bool enable, bool clear) { } void OpenGLRenderer::renderOverdraw() { - if (mCaches.debugOverdraw && getTargetFbo() == 0) { + if (Properties::debugOverdraw && getTargetFbo() == 0) { const Rect* clip = &mTilingClip; mRenderState.scissor().setEnabled(true); @@ -381,7 +381,7 @@ bool OpenGLRenderer::updateLayer(Layer* layer, bool inFrame) { debugOverdraw(false, false); } - if (CC_UNLIKELY(inFrame || mCaches.drawDeferDisabled)) { + if (CC_UNLIKELY(inFrame || Properties::drawDeferDisabled)) { layer->render(*this); } else { layer->defer(*this); @@ -392,7 +392,7 @@ bool OpenGLRenderer::updateLayer(Layer* layer, bool inFrame) { startTilingCurrentClip(); } - layer->debugDrawUpdate = mCaches.debugLayersUpdates; + layer->debugDrawUpdate = Properties::debugLayersUpdates; layer->hasDrawnSinceUpdate = false; return true; @@ -407,7 +407,7 @@ void OpenGLRenderer::updateLayers() { // in the layer updates list which will be cleared by flushLayers(). int count = mLayerUpdates.size(); if (count > 0) { - if (CC_UNLIKELY(mCaches.drawDeferDisabled)) { + if (CC_UNLIKELY(Properties::drawDeferDisabled)) { startMark("Layer Updates"); } else { startMark("Defer Layer Updates"); @@ -419,7 +419,7 @@ void OpenGLRenderer::updateLayers() { updateLayer(layer, false); } - if (CC_UNLIKELY(mCaches.drawDeferDisabled)) { + if (CC_UNLIKELY(Properties::drawDeferDisabled)) { mLayerUpdates.clear(); mRenderState.bindFramebuffer(getTargetFbo()); } @@ -883,13 +883,13 @@ void OpenGLRenderer::composeLayerRect(Layer* layer, const Rect& rect, bool swap) * operations are correctly counted twice for overdraw. NOTE: assumes composeLayerRegion only used * by saveLayer's restore */ -#define DRAW_DOUBLE_STENCIL_IF(COND, DRAW_COMMAND) { \ - DRAW_COMMAND; \ - if (CC_UNLIKELY(mCaches.debugOverdraw && getTargetFbo() == 0 && COND)) { \ - glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); \ - DRAW_COMMAND; \ - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); \ - } \ +#define DRAW_DOUBLE_STENCIL_IF(COND, DRAW_COMMAND) { \ + DRAW_COMMAND; \ + if (CC_UNLIKELY(Properties::debugOverdraw && getTargetFbo() == 0 && COND)) { \ + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); \ + DRAW_COMMAND; \ + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); \ + } \ } #define DRAW_DOUBLE_STENCIL(DRAW_COMMAND) DRAW_DOUBLE_STENCIL_IF(true, DRAW_COMMAND) @@ -1324,7 +1324,7 @@ void OpenGLRenderer::drawRectangleList(const RectangleList& rectangleList) { } void OpenGLRenderer::setStencilFromClip() { - if (!mCaches.debugOverdraw) { + if (!Properties::debugOverdraw) { if (!currentSnapshot()->clipIsSimple()) { int incrementThreshold; EVENT_LOGD("setStencilFromClip - enabling"); @@ -1383,8 +1383,8 @@ void OpenGLRenderer::setStencilFromClip() { // Draw the region used to generate the stencil if the appropriate debug // mode is enabled // TODO: Implement for rectangle list clip areas - if (mCaches.debugStencilClip == Caches::kStencilShowRegion && - !clipArea.isRectangleList()) { + if (Properties::debugStencilClip == StencilClipDebug::ShowRegion + && !clipArea.isRectangleList()) { paint.setColor(0x7f0000ff); paint.setXfermodeMode(SkXfermode::kSrcOver_Mode); drawRegionRects(currentSnapshot()->getClipRegion(), paint); @@ -1469,7 +1469,7 @@ void OpenGLRenderer::drawRenderNode(RenderNode* renderNode, Rect& dirty, int32_t if (renderNode && renderNode->isRenderable()) { // compute 3d ordering renderNode->computeOrdering(); - if (CC_UNLIKELY(mCaches.drawDeferDisabled)) { + if (CC_UNLIKELY(Properties::drawDeferDisabled)) { startFrame(); ReplayStateStruct replayStruct(*this, dirty, replayFlags); renderNode->replay(replayStruct, 0); @@ -1478,7 +1478,7 @@ void OpenGLRenderer::drawRenderNode(RenderNode* renderNode, Rect& dirty, int32_t // Don't avoid overdraw when visualizing, since that makes it harder to // debug where it's coming from, and when the problem occurs. - bool avoidOverdraw = !mCaches.debugOverdraw; + bool avoidOverdraw = !Properties::debugOverdraw; DeferredDisplayList deferredList(mState.currentClipRect(), avoidOverdraw); DeferStateStruct deferStruct(deferredList, *this, replayFlags); renderNode->defer(deferStruct, 0); @@ -2452,8 +2452,8 @@ void OpenGLRenderer::drawShadow(float casterAlpha, // The caller has made sure casterAlpha > 0. float ambientShadowAlpha = mAmbientShadowAlpha; - if (CC_UNLIKELY(mCaches.propertyAmbientShadowStrength >= 0)) { - ambientShadowAlpha = mCaches.propertyAmbientShadowStrength; + if (CC_UNLIKELY(Properties::overrideAmbientShadowStrength >= 0)) { + ambientShadowAlpha = Properties::overrideAmbientShadowStrength; } if (ambientShadowVertexBuffer && ambientShadowAlpha > 0) { paint.setARGB(casterAlpha * ambientShadowAlpha, 0, 0, 0); @@ -2461,8 +2461,8 @@ void OpenGLRenderer::drawShadow(float casterAlpha, } float spotShadowAlpha = mSpotShadowAlpha; - if (CC_UNLIKELY(mCaches.propertySpotShadowStrength >= 0)) { - spotShadowAlpha = mCaches.propertySpotShadowStrength; + if (CC_UNLIKELY(Properties::overrideSpotShadowStrength >= 0)) { + spotShadowAlpha = Properties::overrideSpotShadowStrength; } if (spotShadowVertexBuffer && spotShadowAlpha > 0) { paint.setARGB(casterAlpha * spotShadowAlpha, 0, 0, 0); diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp index bdb44a6..74964f6 100644 --- a/libs/hwui/PathCache.cpp +++ b/libs/hwui/PathCache.cpp @@ -153,7 +153,7 @@ PathCache::PathCache(): glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize); mMaxTextureSize = maxTextureSize; - mDebugEnabled = readDebugLevel() & kDebugCaches; + mDebugEnabled = Properties::debugLevel & kDebugCaches; } PathCache::~PathCache() { diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp new file mode 100644 index 0000000..fd32b6f --- /dev/null +++ b/libs/hwui/Properties.cpp @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "Properties.h" + +#include "Debug.h" + +#include <cmath> +#include <cutils/log.h> + +namespace android { +namespace uirenderer { + +bool Properties::drawDeferDisabled = false; +bool Properties::drawReorderDisabled = false; +bool Properties::debugLayersUpdates = false; +bool Properties::debugOverdraw = false; +bool Properties::showDirtyRegions = false; + +DebugLevel Properties::debugLevel = kDebugDisabled; +OverdrawColorSet Properties::overdrawColorSet = OverdrawColorSet::Default; +StencilClipDebug Properties::debugStencilClip = StencilClipDebug::Hide; + +float Properties::overrideLightRadius = -1.0f; +float Properties::overrideLightPosY = -1.0f; +float Properties::overrideLightPosZ = -1.0f; +float Properties::overrideAmbientRatio = -1.0f; +int Properties::overrideAmbientShadowStrength = -1; +int Properties::overrideSpotShadowStrength = -1; + +ProfileType Properties::sProfileType = ProfileType::None; +bool Properties::sDisableProfileBars = false; + +bool Properties::load() { + char property[PROPERTY_VALUE_MAX]; + bool prevDebugLayersUpdates = debugLayersUpdates; + bool prevDebugOverdraw = debugOverdraw; + StencilClipDebug prevDebugStencilClip = debugStencilClip; + + + debugOverdraw = false; + if (property_get(PROPERTY_DEBUG_OVERDRAW, property, nullptr) > 0) { + INIT_LOGD(" Overdraw debug enabled: %s", property); + if (!strcmp(property, "show")) { + debugOverdraw = true; + overdrawColorSet = OverdrawColorSet::Default; + } else if (!strcmp(property, "show_deuteranomaly")) { + debugOverdraw = true; + overdrawColorSet = OverdrawColorSet::Deuteranomaly; + } + } + + // See Properties.h for valid values + if (property_get(PROPERTY_DEBUG_STENCIL_CLIP, property, nullptr) > 0) { + INIT_LOGD(" Stencil clip debug enabled: %s", property); + if (!strcmp(property, "hide")) { + debugStencilClip = StencilClipDebug::Hide; + } else if (!strcmp(property, "highlight")) { + debugStencilClip = StencilClipDebug::ShowHighlight; + } else if (!strcmp(property, "region")) { + debugStencilClip = StencilClipDebug::ShowRegion; + } + } else { + debugStencilClip = StencilClipDebug::Hide; + } + + sProfileType = ProfileType::None; + if (property_get(PROPERTY_PROFILE, property, "") > 0) { + if (!strcmp(property, PROPERTY_PROFILE_VISUALIZE_BARS)) { + sProfileType = ProfileType::Bars; + } else if (!strcmp(property, "true")) { + sProfileType = ProfileType::Console; + } + } + + debugLayersUpdates = property_get_bool(PROPERTY_DEBUG_LAYERS_UPDATES, false); + INIT_LOGD(" Layers updates debug enabled: %d", debugLayersUpdates); + + drawDeferDisabled = property_get_bool(PROPERTY_DISABLE_DRAW_DEFER, false); + INIT_LOGD(" Draw defer %s", drawDeferDisabled ? "disabled" : "enabled"); + + drawReorderDisabled = property_get_bool(PROPERTY_DISABLE_DRAW_REORDER, false); + INIT_LOGD(" Draw reorder %s", drawReorderDisabled ? "disabled" : "enabled"); + + showDirtyRegions = property_get_bool(PROPERTY_DEBUG_SHOW_DIRTY_REGIONS, false); + + debugLevel = kDebugDisabled; + if (property_get(PROPERTY_DEBUG, property, nullptr) > 0) { + debugLevel = (DebugLevel) atoi(property); + } + + return (prevDebugLayersUpdates != debugLayersUpdates) + || (prevDebugOverdraw != debugOverdraw) + || (prevDebugStencilClip != debugStencilClip); +} + +void Properties::overrideProperty(const char* name, const char* value) { + if (!strcmp(name, "disableProfileBars")) { + sDisableProfileBars = !strcmp(value, "true"); + ALOGD("profile bars %s", sDisableProfileBars ? "disabled" : "enabled"); + return; + } else if (!strcmp(name, "ambientRatio")) { + overrideAmbientRatio = fmin(fmax(atof(value), 0.0), 10.0); + ALOGD("ambientRatio = %.2f", overrideAmbientRatio); + return; + } else if (!strcmp(name, "lightRadius")) { + overrideLightRadius = fmin(fmax(atof(value), 0.0), 3000.0); + ALOGD("lightRadius = %.2f", overrideLightRadius); + return; + } else if (!strcmp(name, "lightPosY")) { + overrideLightPosY = fmin(fmax(atof(value), 0.0), 3000.0); + ALOGD("lightPos Y = %.2f", overrideLightPosY); + return; + } else if (!strcmp(name, "lightPosZ")) { + overrideLightPosZ = fmin(fmax(atof(value), 0.0), 3000.0); + ALOGD("lightPos Z = %.2f", overrideLightPosZ); + return; + } else if (!strcmp(name, "ambientShadowStrength")) { + overrideAmbientShadowStrength = atoi(value); + ALOGD("ambient shadow strength = 0x%x out of 0xff", overrideAmbientShadowStrength); + return; + } else if (!strcmp(name, "spotShadowStrength")) { + overrideSpotShadowStrength = atoi(value); + ALOGD("spot shadow strength = 0x%x out of 0xff", overrideSpotShadowStrength); + return; + } + ALOGD("failed overriding property %s to %s", name, value); +} + +ProfileType Properties::getProfileType() { + if (CC_UNLIKELY(sDisableProfileBars && sProfileType == ProfileType::Bars)) + return ProfileType::None; + return sProfileType; +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index a0312e1..46fa940 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -19,12 +19,16 @@ #include <cutils/properties.h> #include <stdlib.h> +#include <utils/Singleton.h> /** * This file contains the list of system properties used to configure * the OpenGLRenderer. */ +namespace android { +namespace uirenderer { + /////////////////////////////////////////////////////////////////////////////// // Compile-time properties /////////////////////////////////////////////////////////////////////////////// @@ -253,12 +257,61 @@ enum DebugLevel { // Converts a number of kilo-bytes into bytes #define KB(s) s * 1024 -static inline DebugLevel readDebugLevel() { - char property[PROPERTY_VALUE_MAX]; - if (property_get(PROPERTY_DEBUG, property, nullptr) > 0) { - return (DebugLevel) atoi(property); - } - return kDebugDisabled; -} +enum class ProfileType { + None, + Console, + Bars +}; + +enum class OverdrawColorSet { + Default = 0, + Deuteranomaly +}; + +enum class StencilClipDebug { + Hide, + ShowHighlight, + ShowRegion +}; + +/** + * Renderthread-only singleton which manages several static rendering properties. Most of these + * are driven by system properties which are queried once at initialization, and again if init() + * is called. + */ +class Properties { +public: + static bool load(); + + static bool drawDeferDisabled; + static bool drawReorderDisabled; + static bool debugLayersUpdates; + static bool debugOverdraw; + static bool showDirtyRegions; + + static DebugLevel debugLevel; + static OverdrawColorSet overdrawColorSet; + static StencilClipDebug debugStencilClip; + + // Override the value for a subset of properties in this class + static void overrideProperty(const char* name, const char* value); + + static float overrideLightRadius; + static float overrideLightPosY; + static float overrideLightPosZ; + static float overrideAmbientRatio; + static int overrideAmbientShadowStrength; + static int overrideSpotShadowStrength; + + static ProfileType getProfileType(); + +private: + static ProfileType sProfileType; + static bool sDisableProfileBars; + +}; // class Caches + +}; // namespace uirenderer +}; // namespace android #endif // ANDROID_HWUI_PROPERTIES_H diff --git a/libs/hwui/ShadowTessellator.cpp b/libs/hwui/ShadowTessellator.cpp index 30d3f41..fb28531 100644 --- a/libs/hwui/ShadowTessellator.cpp +++ b/libs/hwui/ShadowTessellator.cpp @@ -20,11 +20,13 @@ #include <math.h> #include <utils/Log.h> #include <utils/Trace.h> +#include <utils/Vector.h> #include "AmbientShadow.h" -#include "Caches.h" +#include "Properties.h" #include "ShadowTessellator.h" #include "SpotShadow.h" +#include "Vector.h" namespace android { namespace uirenderer { @@ -40,9 +42,8 @@ void ShadowTessellator::tessellateAmbientShadow(bool isCasterOpaque, float heightFactor = 1.0f / 128; const float geomFactor = 64; - Caches& caches = Caches::getInstance(); - if (CC_UNLIKELY(caches.propertyAmbientRatio > 0.0f)) { - heightFactor *= caches.propertyAmbientRatio; + if (CC_UNLIKELY(Properties::overrideAmbientRatio > 0.0f)) { + heightFactor *= Properties::overrideAmbientRatio; } Rect ambientShadowBounds(casterBounds); @@ -66,14 +67,12 @@ void ShadowTessellator::tessellateSpotShadow(bool isCasterOpaque, const Rect& casterBounds, const Rect& localClip, VertexBuffer& shadowVertexBuffer) { ATRACE_CALL(); - Caches& caches = Caches::getInstance(); - Vector3 adjustedLightCenter(lightCenter); - if (CC_UNLIKELY(caches.propertyLightPosY > 0)) { - adjustedLightCenter.y = - caches.propertyLightPosY; // negated since this shifts up + if (CC_UNLIKELY(Properties::overrideLightPosY > 0)) { + adjustedLightCenter.y = - Properties::overrideLightPosY; // negated since this shifts up } - if (CC_UNLIKELY(caches.propertyLightPosZ > 0)) { - adjustedLightCenter.z = caches.propertyLightPosZ; + if (CC_UNLIKELY(Properties::overrideLightPosZ > 0)) { + adjustedLightCenter.z = Properties::overrideLightPosZ; } #if DEBUG_SHADOW @@ -87,8 +86,8 @@ void ShadowTessellator::tessellateSpotShadow(bool isCasterOpaque, reverseReceiverTransform.loadInverse(receiverTransform); reverseReceiverTransform.mapPoint3d(adjustedLightCenter); - if (CC_UNLIKELY(caches.propertyLightRadius > 0)) { - lightRadius = caches.propertyLightRadius; + if (CC_UNLIKELY(Properties::overrideLightRadius > 0)) { + lightRadius = Properties::overrideLightRadius; } // Now light and caster are both in local space, we will check whether diff --git a/libs/hwui/TessellationCache.cpp b/libs/hwui/TessellationCache.cpp index 7edb9fb..fc173f7 100644 --- a/libs/hwui/TessellationCache.cpp +++ b/libs/hwui/TessellationCache.cpp @@ -311,7 +311,7 @@ TessellationCache::TessellationCache() mCache.setOnEntryRemovedListener(&mBufferRemovedListener); mShadowCache.setOnEntryRemovedListener(&mBufferPairRemovedListener); - mDebugEnabled = readDebugLevel() & kDebugCaches; + mDebugEnabled = Properties::debugLevel & kDebugCaches; } TessellationCache::~TessellationCache() { diff --git a/libs/hwui/TextDropShadowCache.cpp b/libs/hwui/TextDropShadowCache.cpp index c2e88f3..8b1d4cb 100644 --- a/libs/hwui/TextDropShadowCache.cpp +++ b/libs/hwui/TextDropShadowCache.cpp @@ -123,7 +123,7 @@ TextDropShadowCache::~TextDropShadowCache() { void TextDropShadowCache::init() { mCache.setOnEntryRemovedListener(this); - mDebugEnabled = readDebugLevel() & kDebugMoreCaches; + mDebugEnabled = Properties::debugLevel & kDebugMoreCaches; } /////////////////////////////////////////////////////////////////////////////// diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp index b911a0f..e59688c 100644 --- a/libs/hwui/TextureCache.cpp +++ b/libs/hwui/TextureCache.cpp @@ -66,7 +66,7 @@ TextureCache::TextureCache() glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize); INIT_LOGD(" Maximum texture dimension is %d pixels", mMaxTextureSize); - mDebugEnabled = readDebugLevel() & kDebugCaches; + mDebugEnabled = Properties::debugLevel & kDebugCaches; } TextureCache::~TextureCache() { diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp index 7b44d6d..e54fa5a 100644 --- a/libs/hwui/renderstate/RenderState.cpp +++ b/libs/hwui/renderstate/RenderState.cpp @@ -154,7 +154,7 @@ void RenderState::resumeFromFunctorInvoke() { } void RenderState::debugOverdraw(bool enable, bool clear) { - if (mCaches->debugOverdraw && mFramebuffer == 0) { + if (Properties::debugOverdraw && mFramebuffer == 0) { if (clear) { scissor().setEnabled(false); stencil().clear(); diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index 7c04f40..c643e1d 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -112,9 +112,9 @@ void RenderProxy::setSwapBehavior(SwapBehavior swapBehavior) { CREATE_BRIDGE1(loadSystemProperties, CanvasContext* context) { bool needsRedraw = false; if (Caches::hasInstance()) { - needsRedraw = Caches::getInstance().initProperties(); + needsRedraw = Properties::load(); } - if (args->context->profiler().loadSystemProperties()) { + if (args->context->profiler().consumeProperties()) { needsRedraw = true; } return (void*) needsRedraw; @@ -135,7 +135,7 @@ void RenderProxy::setName(const char* name) { SETUP_TASK(setName); args->context = mContext; args->name = name; - postAndWait(task); + postAndWait(task); // block since name/value pointers owned by caller } CREATE_BRIDGE2(initialize, CanvasContext* context, ANativeWindow* window) { @@ -331,7 +331,7 @@ void RenderProxy::destroyHardwareResources() { post(task); } -CREATE_BRIDGE2(timMemory, RenderThread* thread, int level) { +CREATE_BRIDGE2(trimMemory, RenderThread* thread, int level) { CanvasContext::trimMemory(*args->thread, args->level); return nullptr; } @@ -340,13 +340,26 @@ void RenderProxy::trimMemory(int level) { // Avoid creating a RenderThread to do a trimMemory. if (RenderThread::hasInstance()) { RenderThread& thread = RenderThread::getInstance(); - SETUP_TASK(timMemory); + SETUP_TASK(trimMemory); args->thread = &thread; args->level = level; thread.queue(task); } } +CREATE_BRIDGE2(overrideProperty, const char* name, const char* value) { + Properties::overrideProperty(args->name, args->value); + return nullptr; +} + +void RenderProxy::overrideProperty(const char* name, const char* value) { + RenderThread& thread = RenderThread::getInstance(); + SETUP_TASK(overrideProperty); + args->name = name; + args->value = value; + staticPostAndWait(task); // expensive, but block here since name/value pointers owned by caller +} + CREATE_BRIDGE0(fence) { // Intentionally empty return nullptr; @@ -383,8 +396,10 @@ void RenderProxy::notifyFramePending() { mRenderThread.queueAtFront(task); } -CREATE_BRIDGE3(dumpProfileInfo, CanvasContext* context, int fd, int dumpFlags) { +CREATE_BRIDGE4(dumpProfileInfo, CanvasContext* context, RenderThread* thread, + int fd, int dumpFlags) { args->context->profiler().dumpData(args->fd); + args->thread->jankTracker().dump(args->fd); if (args->dumpFlags & DumpFlags::kFrameStats) { args->context->dumpFrames(args->fd); } @@ -397,11 +412,23 @@ CREATE_BRIDGE3(dumpProfileInfo, CanvasContext* context, int fd, int dumpFlags) { void RenderProxy::dumpProfileInfo(int fd, int dumpFlags) { SETUP_TASK(dumpProfileInfo); args->context = mContext; + args->thread = &mRenderThread; args->fd = fd; args->dumpFlags = dumpFlags; postAndWait(task); } +CREATE_BRIDGE1(resetProfileInfo, CanvasContext* context) { + args->context->resetFrameStats(); + return nullptr; +} + +void RenderProxy::resetProfileInfo() { + SETUP_TASK(resetProfileInfo); + args->context = mContext; + postAndWait(task); +} + CREATE_BRIDGE2(dumpGraphicsMemory, int fd, RenderThread* thread) { args->thread->jankTracker().dump(args->fd); diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index cc475fa..31456cd 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -90,12 +90,15 @@ public: ANDROID_API void destroyHardwareResources(); ANDROID_API static void trimMemory(int level); + ANDROID_API static void overrideProperty(const char* name, const char* value); ANDROID_API void fence(); ANDROID_API void stopDrawing(); ANDROID_API void notifyFramePending(); ANDROID_API void dumpProfileInfo(int fd, int dumpFlags); + // Not exported, only used for testing + void resetProfileInfo(); ANDROID_API static void dumpGraphicsMemory(int fd); ANDROID_API void setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t size); diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index 3ac2976..64075f1 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -144,6 +144,7 @@ RenderThread::RenderThread() : Thread(true), Singleton<RenderThread>() , mFrameCallbackTask(nullptr) , mRenderState(nullptr) , mEglManager(nullptr) { + Properties::load(); mFrameCallbackTask = new DispatchFrameCallbacks(this); mLooper = new Looper(false); run("RenderThread"); diff --git a/libs/hwui/tests/TestContext.cpp b/libs/hwui/tests/TestContext.cpp index 542bbae..3687a50 100644 --- a/libs/hwui/tests/TestContext.cpp +++ b/libs/hwui/tests/TestContext.cpp @@ -57,6 +57,10 @@ sp<Surface> TestContext::surface() { } void TestContext::waitForVsync() { +#if HWUI_NULL_GPU + return; +#endif + // Request vsync mDisplayEventReceiver.requestNextVsync(); @@ -71,4 +75,3 @@ void TestContext::waitForVsync() { } // namespace test } // namespace uirenderer } // namespace android - diff --git a/libs/hwui/tests/how_to_run.txt b/libs/hwui/tests/how_to_run.txt new file mode 100644 index 0000000..686cd78 --- /dev/null +++ b/libs/hwui/tests/how_to_run.txt @@ -0,0 +1,17 @@ +mmm -j8 frameworks/base/libs/hwui/tests/ && + adb push $OUT/data/local/tmp/hwuitest /data/local/tmp/hwuitest && + adb shell /data/local/tmp/hwuitest + + +Command arguments: +hwuitest [testname] + +Default test is 'shadowgrid' + +List of tests: + +shadowgrid: creates a grid of rounded rects that cast shadows, high CPU & GPU load + +rectgrid: creates a grid of 1x1 rects + +oval: draws 1 oval diff --git a/libs/hwui/tests/main.cpp b/libs/hwui/tests/main.cpp index 62782af..aca7c52 100644 --- a/libs/hwui/tests/main.cpp +++ b/libs/hwui/tests/main.cpp @@ -14,8 +14,6 @@ * limitations under the License. */ -#include <stdio.h> - #include <cutils/log.h> #include <gui/Surface.h> #include <ui/PixelFormat.h> @@ -28,6 +26,9 @@ #include "TestContext.h" +#include <stdio.h> +#include <unistd.h> + using namespace android; using namespace android::uirenderer; using namespace android::uirenderer::renderthread; @@ -93,16 +94,27 @@ public: animation.createContent(width, height, renderer); endRecording(renderer, rootNode); + // Do a few cold runs then reset the stats so that the caches are all hot + for (int i = 0; i < 3; i++) { + testContext.waitForVsync(); + proxy->syncAndDrawFrame(); + } + proxy->resetProfileInfo(); + for (int i = 0; i < animation.getFrameCount(); i++) { -#if !HWUI_NULL_GPU testContext.waitForVsync(); -#endif + // workaround b/20853441 + proxy->fence(); ATRACE_NAME("UI-Draw Frame"); + nsecs_t vsync = systemTime(CLOCK_MONOTONIC); + UiFrameInfoBuilder(proxy->frameInfo()) + .setVsync(vsync, vsync); animation.doFrame(i); proxy->syncAndDrawFrame(); } + proxy->dumpProfileInfo(STDOUT_FILENO, 0); rootNode->decStrong(nullptr); } }; diff --git a/libs/hwui/unit_tests/Android.mk b/libs/hwui/unit_tests/Android.mk index 51601b0..917e646 100644 --- a/libs/hwui/unit_tests/Android.mk +++ b/libs/hwui/unit_tests/Android.mk @@ -27,6 +27,7 @@ include $(LOCAL_PATH)/Android.common.mk LOCAL_SRC_FILES += \ unit_tests/ClipAreaTests.cpp \ + unit_tests/DamageAccumulatorTests.cpp \ unit_tests/LinearAllocatorTests.cpp \ unit_tests/main.cpp diff --git a/libs/hwui/unit_tests/DamageAccumulatorTests.cpp b/libs/hwui/unit_tests/DamageAccumulatorTests.cpp new file mode 100644 index 0000000..c8d6004 --- /dev/null +++ b/libs/hwui/unit_tests/DamageAccumulatorTests.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include <DamageAccumulator.h> +#include <Matrix.h> +#include <utils/LinearAllocator.h> + +#include <SkRect.h> + +using namespace android; +using namespace android::uirenderer; + +// Test that push & pop are propegating the dirty rect +// There is no transformation of the dirty rect, the input is the same +// as the output. +TEST(DamageAccumulator, identity) { + DamageAccumulator da; + Matrix4 identity; + SkRect curDirty; + identity.loadIdentity(); + da.pushTransform(&identity); + da.dirty(50, 50, 100, 100); + da.pushTransform(&identity); + da.peekAtDirty(&curDirty); + ASSERT_EQ(SkRect(), curDirty); + da.popTransform(); + da.peekAtDirty(&curDirty); + ASSERT_EQ(SkRect::MakeLTRB(50, 50, 100, 100), curDirty); + da.popTransform(); + da.finish(&curDirty); + ASSERT_EQ(SkRect::MakeLTRB(50, 50, 100, 100), curDirty); +} + +// Test that transformation is happening at the correct levels via +// peekAtDirty & popTransform. Just uses a simple translate to test this +TEST(DamageAccumulator, translate) { + DamageAccumulator da; + Matrix4 translate; + SkRect curDirty; + translate.loadTranslate(25, 25, 0); + da.pushTransform(&translate); + da.dirty(50, 50, 100, 100); + da.peekAtDirty(&curDirty); + ASSERT_EQ(SkRect::MakeLTRB(50, 50, 100, 100), curDirty); + da.popTransform(); + da.finish(&curDirty); + ASSERT_EQ(SkRect::MakeLTRB(75, 75, 125, 125), curDirty); +} + +// Test that dirty rectangles are being unioned across "siblings +TEST(DamageAccumulator, union) { + DamageAccumulator da; + Matrix4 identity; + SkRect curDirty; + identity.loadIdentity(); + da.pushTransform(&identity); + da.pushTransform(&identity); + da.dirty(50, 50, 100, 100); + da.popTransform(); + da.pushTransform(&identity); + da.dirty(150, 50, 200, 125); + da.popTransform(); + da.popTransform(); + da.finish(&curDirty); + ASSERT_EQ(SkRect::MakeLTRB(50, 50, 200, 125), curDirty); +} diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java index 974c9af..89d419a 100644 --- a/media/java/android/media/MediaCodecInfo.java +++ b/media/java/android/media/MediaCodecInfo.java @@ -1733,6 +1733,72 @@ public final class MediaCodecInfo { maxBlocks, maxBlocksPerSecond, 16 /* blockWidth */, 16 /* blockHeight */, 1 /* widthAlignment */, 1 /* heightAlignment */); + } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_MPEG2)) { + int maxWidth = 11, maxHeight = 9, maxRate = 15; + maxBlocks = 99; + maxBlocksPerSecond = 1485; + maxBps = 64000; + for (CodecProfileLevel profileLevel: profileLevels) { + int MBPS = 0, FS = 0, BR = 0, FR = 0, W = 0, H = 0; + boolean supported = true; + switch (profileLevel.profile) { + case CodecProfileLevel.MPEG2ProfileSimple: + switch (profileLevel.level) { + case CodecProfileLevel.MPEG2LevelML: + FR = 30; W = 45; H = 36; MBPS = 48600; FS = 1620; BR = 15000; break; + default: + Log.w(TAG, "Unrecognized profile/level " + + profileLevel.profile + "/" + + profileLevel.level + " for " + mime); + errors |= ERROR_UNRECOGNIZED; + } + break; + case CodecProfileLevel.MPEG2ProfileMain: + switch (profileLevel.level) { + case CodecProfileLevel.MPEG2LevelLL: + FR = 30; W = 22; H = 18; MBPS = 11880; FS = 396; BR = 4000; break; + case CodecProfileLevel.MPEG2LevelML: + FR = 30; W = 45; H = 36; MBPS = 48600; FS = 1620; BR = 15000; break; + case CodecProfileLevel.MPEG2LevelH14: + FR = 60; W = 90; H = 68; MBPS = 367200; FS = 6120; BR = 60000; break; + case CodecProfileLevel.MPEG2LevelHL: + FR = 60; W = 120; H = 68; MBPS = 489600; FS = 8160; BR = 80000; break; + default: + Log.w(TAG, "Unrecognized profile/level " + + profileLevel.profile + "/" + + profileLevel.level + " for " + mime); + errors |= ERROR_UNRECOGNIZED; + } + break; + case CodecProfileLevel.MPEG2Profile422: + case CodecProfileLevel.MPEG2ProfileSNR: + case CodecProfileLevel.MPEG2ProfileSpatial: + case CodecProfileLevel.MPEG2ProfileHigh: + Log.i(TAG, "Unsupported profile " + + profileLevel.profile + " for " + mime); + errors |= ERROR_UNSUPPORTED; + supported = false; + break; + default: + Log.w(TAG, "Unrecognized profile " + + profileLevel.profile + " for " + mime); + errors |= ERROR_UNRECOGNIZED; + } + if (supported) { + errors &= ~ERROR_NONE_SUPPORTED; + } + maxBlocksPerSecond = Math.max(MBPS, maxBlocksPerSecond); + maxBlocks = Math.max(FS, maxBlocks); + maxBps = Math.max(BR * 1000, maxBps); + maxWidth = Math.max(W, maxWidth); + maxHeight = Math.max(H, maxHeight); + maxRate = Math.max(FR, maxRate); + } + applyMacroBlockLimits(maxWidth, maxHeight, + maxBlocks, maxBlocksPerSecond, + 16 /* blockWidth */, 16 /* blockHeight */, + 1 /* widthAlignment */, 1 /* heightAlignment */); + mFrameRateRange = mFrameRateRange.intersect(12, maxRate); } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_MPEG4)) { int maxWidth = 11, maxHeight = 9, maxRate = 15; maxBlocks = 99; @@ -2343,6 +2409,20 @@ public final class MediaCodecInfo { public static final int MPEG4Level4a = 0x40; public static final int MPEG4Level5 = 0x80; + // from OMX_VIDEO_MPEG2PROFILETYPE + public static final int MPEG2ProfileSimple = 0x00; + public static final int MPEG2ProfileMain = 0x01; + public static final int MPEG2Profile422 = 0x02; + public static final int MPEG2ProfileSNR = 0x03; + public static final int MPEG2ProfileSpatial = 0x04; + public static final int MPEG2ProfileHigh = 0x05; + + // from OMX_VIDEO_MPEG2LEVELTYPE + public static final int MPEG2LevelLL = 0x00; + public static final int MPEG2LevelML = 0x01; + public static final int MPEG2LevelH14 = 0x02; + public static final int MPEG2LevelHL = 0x03; + // from OMX_AUDIO_AACPROFILETYPE public static final int AACObjectMain = 1; public static final int AACObjectLC = 2; diff --git a/media/java/android/media/tv/TvContentRating.java b/media/java/android/media/tv/TvContentRating.java index daeb1cc..043b80e 100644 --- a/media/java/android/media/tv/TvContentRating.java +++ b/media/java/android/media/tv/TvContentRating.java @@ -783,10 +783,15 @@ public final class TvContentRating { private final int mHashCode; /** - * Rating constant denoting unrated content. + * Rating constant denoting unrated content. Used to handle the case where the content rating + * information is missing. + * + * <p>TV input services can call {@link TvInputManager#isRatingBlocked} with this constant to + * determine whether they should block unrated content. The subsequent call to + * {@link TvInputService.Session#notifyContentBlocked} with the same constant notifies + * applications that the current program content is blocked by parental controls. */ - public static final TvContentRating UNRATED = new TvContentRating("com.android.tv", "", - "UNRATED", null); + public static final TvContentRating UNRATED = new TvContentRating("null", "null", "null", null); /** * Creates a {@code TvContentRating} object with predefined content rating strings. diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java index 3272a23..dca0631 100644 --- a/media/java/android/media/tv/TvInputManager.java +++ b/media/java/android/media/tv/TvInputManager.java @@ -1035,7 +1035,7 @@ public final class TvInputManager { /** * Checks whether a given TV content rating is blocked by the user. * - * @param rating The TV content rating to check. + * @param rating The TV content rating to check. Can be {@link TvContentRating#UNRATED}. * @return {@code true} if the given TV content rating is blocked, {@code false} otherwise. */ public boolean isRatingBlocked(@NonNull TvContentRating rating) { diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java index 34c36c3..c1035b0 100644 --- a/media/java/android/media/tv/TvInputService.java +++ b/media/java/android/media/tv/TvInputService.java @@ -528,11 +528,12 @@ public abstract class TvInputService extends Service { * TvInputManager.isParentalControlsEnabled()} returns {@code true}). Whether the TV input * service should block the content or not is determined by invoking * {@link TvInputManager#isRatingBlocked TvInputManager.isRatingBlocked(TvContentRating)} - * with the content rating for the current program. Then the {@link TvInputManager} makes a - * judgment based on the user blocked ratings stored in the secure settings and returns the - * result. If the rating in question turns out to be blocked, the TV input service must - * immediately block the content and call this method with the content rating of the current - * program to prompt the PIN verification screen. + * with the content rating for the current program or {@link TvContentRating#UNRATED} in + * case the rating information is missing. Then the {@link TvInputManager} makes a judgment + * based on the user blocked ratings stored in the secure settings and returns the result. + * If the rating in question turns out to be blocked, the TV input service must immediately + * block the content and call this method with the content rating of the current program to + * prompt the PIN verification screen. * * <p>Each TV input service also needs to continuously listen to any changes made to the * parental controls settings by registering a broadcast receiver to receive @@ -540,7 +541,8 @@ public abstract class TvInputService extends Service { * {@link TvInputManager#ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED} and immediately * reevaluate the current program with the new parental controls settings. * - * @param rating The content rating for the current TV program. + * @param rating The content rating for the current TV program. Can be + * {@link TvContentRating#UNRATED}. * @see #notifyContentAllowed * @see TvInputManager */ diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java index 86c23c7..16b4c43 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java @@ -60,6 +60,7 @@ public class MediaNames { public static final String VIDEO_H264_AAC = "/sdcard/media_api/video/H264_320_AAC_64.3gp"; public static final String VIDEO_H264_AMR = "/sdcard/media_api/video/H264_320_AMRNB_6.3gp"; public static final String VIDEO_HEVC_AAC = "/sdcard/media_api/video/HEVC_320_AAC_128.mp4"; + public static final String VIDEO_MPEG2_AAC = "/sdcard/media_api/video/MPEG2_1500_AAC_128.mp4"; public static final String VIDEO_HIGHRES_H263 = "/sdcard/media_api/video/H263_500_AMRNB_12.3gp"; public static final String VIDEO_HIGHRES_MP4 = "/sdcard/media_api/video/H264_500_AAC_128.3gp"; public static final String VIDEO_WEBM = "/sdcard/media_api/video/big-buck-bunny_trailer.webm"; diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java index 244b07f..c528165 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java @@ -432,7 +432,22 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med assertTrue("HEVC playback memory test", memoryResult); } - // Test case 4: Capture the memory usage after every 20 video only recorded + // Test case 4: Capture the memory usage after every 20 mpeg2 playback + @LargeTest + public void testMPEG2VideoPlaybackMemoryUsage() throws Exception { + boolean memoryResult = false; + + mStartPid = getMediaserverPid(); + for (int i = 0; i < NUM_STRESS_LOOP; i++) { + mediaStressPlayback(MediaNames.VIDEO_MPEG2_AAC); + getMemoryWriteToLog(i); + writeProcmemInfo(); + } + memoryResult = validateMemoryResult(mStartPid, mStartMemory, DECODER_LIMIT); + assertTrue("MPEG2 playback memory test", memoryResult); + } + + // Test case 5: Capture the memory usage after every 20 video only recorded @LargeTest public void testH263RecordVideoOnlyMemoryUsage() throws Exception { if (mCamcorderProfile != null) { @@ -453,7 +468,7 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med } } - // Test case 5: Capture the memory usage after every 20 video only recorded + // Test case 6: Capture the memory usage after every 20 video only recorded @LargeTest public void testMpeg4RecordVideoOnlyMemoryUsage() throws Exception { if (mCamcorderProfile != null) { @@ -474,7 +489,7 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med } } - // Test case 6: Capture the memory usage after every 20 video and audio + // Test case 7: Capture the memory usage after every 20 video and audio // recorded @LargeTest public void testRecordVideoAudioMemoryUsage() throws Exception { @@ -496,7 +511,7 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med } } - // Test case 7: Capture the memory usage after every 20 audio only recorded + // Test case 8: Capture the memory usage after every 20 audio only recorded @LargeTest public void testRecordAudioOnlyMemoryUsage() throws Exception { boolean memoryResult = false; @@ -511,7 +526,7 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med assertTrue("audio record only memory test", memoryResult); } - // Test case 8: Capture the memory usage after every 20 camera preview + // Test case 9: Capture the memory usage after every 20 camera preview @LargeTest public void testCameraPreviewMemoryUsage() throws Exception { boolean memoryResult = false; diff --git a/media/tests/contents/media_api/video/MPEG2_1500_AAC_128.mp4 b/media/tests/contents/media_api/video/MPEG2_1500_AAC_128.mp4 Binary files differnew file mode 100644 index 0000000..33f66a0 --- /dev/null +++ b/media/tests/contents/media_api/video/MPEG2_1500_AAC_128.mp4 diff --git a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java index 2e0bece..202402f 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java +++ b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java @@ -449,7 +449,7 @@ public class CopyService extends IntentService { InputStream src = null; OutputStream dst = null; - boolean errorOccurred = false; + IOException copyError = null; try { srcFile = mSrcClient.openFile(srcUri, "r", canceller); dstFile = mDstClient.openFile(dstUri, "w", canceller); @@ -462,16 +462,14 @@ public class CopyService extends IntentService { dst.write(buffer, 0, len); makeProgress(len); } + srcFile.checkError(); - dstFile.checkError(); } catch (IOException e) { - errorOccurred = true; - Log.e(TAG, "Error while copying " + srcUri.toString(), e); + copyError = e; try { - mFailedFiles.add(DocumentInfo.fromUri(getContentResolver(), srcUri)); - } catch (FileNotFoundException ignore) { - Log.w(TAG, "Source file gone: " + srcUri, e); - // The source file is gone. + dstFile.closeWithError(copyError.getMessage()); + } catch (IOException closeError) { + Log.e(TAG, "Error closing destination", closeError); } } finally { // This also ensures the file descriptors are closed. @@ -479,7 +477,18 @@ public class CopyService extends IntentService { IoUtils.closeQuietly(dst); } - if (errorOccurred || mIsCancelled) { + if (copyError != null) { + // Log errors. + Log.e(TAG, "Error while copying " + srcUri.toString(), copyError); + try { + mFailedFiles.add(DocumentInfo.fromUri(getContentResolver(), srcUri)); + } catch (FileNotFoundException ignore) { + Log.w(TAG, "Source file gone: " + srcUri, copyError); + // The source file is gone. + } + } + + if (copyError != null || mIsCancelled) { // Clean up half-copied files. canceller.cancel(); try { diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/CopyTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/CopyTest.java index 13f7daa..b1c84dd 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/CopyTest.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/CopyTest.java @@ -16,23 +16,21 @@ package com.android.documentsui; -import static com.android.documentsui.model.DocumentInfo.getCursorString; - -import android.app.NotificationManager; import android.content.ContentProviderClient; import android.content.ContentResolver; import android.content.Context; import android.content.ContextWrapper; import android.content.Intent; +import android.content.pm.ProviderInfo; +import android.database.ContentObserver; import android.database.Cursor; import android.net.Uri; -import android.os.ParcelFileDescriptor; import android.os.Parcelable; import android.os.RemoteException; import android.provider.DocumentsContract; -import android.provider.DocumentsContract.Document; import android.test.MoreAsserts; import android.test.ServiceTestCase; +import android.test.mock.MockContentResolver; import android.util.Log; import com.android.documentsui.model.DocumentInfo; @@ -43,40 +41,93 @@ import com.google.common.collect.Lists; import libcore.io.IoUtils; import libcore.io.Streams; -import org.mockito.Mockito; - +import java.io.File; +import java.io.FileInputStream; import java.io.FileNotFoundException; -import java.io.InputStream; -import java.io.OutputStream; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; public class CopyTest extends ServiceTestCase<CopyService> { + /** + * A test resolver that enables this test suite to listen for notifications that mark when copy + * operations are done. + */ + class TestContentResolver extends MockContentResolver { + private CountDownLatch mReadySignal; + private CountDownLatch mNotificationSignal; + + public TestContentResolver() { + mReadySignal = new CountDownLatch(1); + } + + /** + * Wait for the given number of files to be copied to destination. Times out after 1 sec. + */ + public void waitForChanges(int count) throws Exception { + // Wait for no more than 1 second by default. + waitForChanges(count, 1000); + } + + /** + * Wait for files to be copied to destination. + * + * @param count Number of files to wait for. + * @param timeOut Timeout in ms. TimeoutException will be thrown if this function times out. + */ + public void waitForChanges(int count, int timeOut) throws Exception { + mNotificationSignal = new CountDownLatch(count); + // Signal that the test is now waiting for files. + mReadySignal.countDown(); + if (!mNotificationSignal.await(timeOut, TimeUnit.MILLISECONDS)) { + throw new TimeoutException("Timed out waiting for files to be copied."); + } + } + + @Override + public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) { + // Wait until the test is ready to receive file notifications. + try { + mReadySignal.await(); + } catch (InterruptedException e) { + Log.d(TAG, "Interrupted while waiting for file copy readiness"); + Thread.currentThread().interrupt(); + } + if (DocumentsContract.isDocumentUri(mContext, uri)) { + Log.d(TAG, "Notification: " + uri); + // Watch for document URI change notifications - this signifies the end of a copy. + mNotificationSignal.countDown(); + } + } + }; + public CopyTest() { super(CopyService.class); } - private static String TAG = "CopyTest"; - // This must match the authority for the StubProvider. private static String AUTHORITY = "com.android.documentsui.stubprovider"; + private static String DST = "sd1"; + private static String SRC = "sd0"; + private static String TAG = "CopyTest"; private List<RootInfo> mRoots; private Context mContext; - private ContentResolver mResolver; + private TestContentResolver mResolver; private ContentProviderClient mClient; - private NotificationManager mNotificationManager; + private StubProvider mStorage; + private Context mSystemContext; @Override protected void setUp() throws Exception { super.setUp(); - setupTestContext(); - mResolver = mContext.getContentResolver(); + setupTestContext(); mClient = mResolver.acquireContentProviderClient(AUTHORITY); // Reset the stub provider's storage. - mClient.call("clear", "", null); + mStorage.clearCacheAndBuildRoots(); mRoots = Lists.newArrayList(); Uri queryUri = DocumentsContract.buildRootsUri(AUTHORITY); @@ -84,9 +135,7 @@ public class CopyTest extends ServiceTestCase<CopyService> { try { cursor = mClient.query(queryUri, null, null, null, null); while (cursor.moveToNext()) { - final RootInfo root = RootInfo.fromRootsCursor(AUTHORITY, cursor); - final String id = root.rootId; - mRoots.add(root); + mRoots.add(RootInfo.fromRootsCursor(AUTHORITY, cursor)); } } finally { IoUtils.closeQuietly(cursor); @@ -100,68 +149,94 @@ public class CopyTest extends ServiceTestCase<CopyService> { super.tearDown(); } - public List<Uri> setupTestFiles() throws Exception { - Uri rootUri = DocumentsContract.buildDocumentUri(AUTHORITY, mRoots.get(0).documentId); - List<Uri> testFiles = Lists.newArrayList( - DocumentsContract.createDocument(mClient, rootUri, "text/plain", "test0.txt"), - DocumentsContract.createDocument(mClient, rootUri, "text/plain", "test1.txt"), - DocumentsContract.createDocument(mClient, rootUri, "text/plain", "test2.txt") - ); - String testContent[] = { - "The five boxing wizards jump quickly", - "The quick brown fox jumps over the lazy dog", - "Jackdaws love my big sphinx of quartz" - }; - for (int i = 0; i < testFiles.size(); ++i) { - ParcelFileDescriptor pfd = null; - OutputStream out = null; - try { - pfd = mClient.openFile(testFiles.get(i), "w"); - out = new ParcelFileDescriptor.AutoCloseOutputStream(pfd); - out.write(testContent[i].getBytes()); - } finally { - IoUtils.closeQuietly(out); - } - } - return testFiles; - } - /** * Test copying a single file. */ public void testCopyFile() throws Exception { - Uri testFile = setupTestFiles().get(0); + String srcPath = "/test0.txt"; + Uri testFile = mStorage.createFile(SRC, srcPath, "text/plain", + "The five boxing wizards jump quickly".getBytes()); + + assertDstFileCountEquals(0); - // Just copy one file. copyToDestination(Lists.newArrayList(testFile)); - // A call to NotificationManager.cancel marks the end of the copy operation. - Mockito.verify(mNotificationManager, Mockito.timeout(1000)).cancel(Mockito.anyString(), - Mockito.anyInt()); + // 2 operations: file creation, then writing data. + mResolver.waitForChanges(2); // Verify that one file was copied; check file contents. assertDstFileCountEquals(1); - assertCopied(testFile); + assertCopied(srcPath); } /** * Test copying multiple files. */ public void testCopyMultipleFiles() throws Exception { - List<Uri> testFiles = setupTestFiles(); + String testContent[] = { + "The five boxing wizards jump quickly", + "The quick brown fox jumps over the lazy dog", + "Jackdaws love my big sphinx of quartz" + }; + String srcPaths[] = { + "/test0.txt", + "/test1.txt", + "/test2.txt" + }; + List<Uri> testFiles = Lists.newArrayList( + mStorage.createFile(SRC, srcPaths[0], "text/plain", testContent[0].getBytes()), + mStorage.createFile(SRC, srcPaths[1], "text/plain", testContent[1].getBytes()), + mStorage.createFile(SRC, srcPaths[2], "text/plain", testContent[2].getBytes())); + + assertDstFileCountEquals(0); + // Copy all the test files. copyToDestination(testFiles); - // A call to NotificationManager.cancel marks the end of the copy operation. - Mockito.verify(mNotificationManager, Mockito.timeout(1000)).cancel(Mockito.anyString(), - Mockito.anyInt()); + // 3 file creations, 3 file writes. + mResolver.waitForChanges(6); assertDstFileCountEquals(3); - for (Uri testFile : testFiles) { - assertCopied(testFile); + for (String path : srcPaths) { + assertCopied(path); } } + public void testCopyEmptyDir() throws Exception { + String srcPath = "/emptyDir"; + Uri testDir = mStorage.createFile(SRC, srcPath, DocumentsContract.Document.MIME_TYPE_DIR, + null); + + assertDstFileCountEquals(0); + + copyToDestination(Lists.newArrayList(testDir)); + + // Just 1 operation: Directory creation. + mResolver.waitForChanges(1); + + assertDstFileCountEquals(1); + + File dst = mStorage.getFile(DST, srcPath); + assertTrue(dst.isDirectory()); + } + + public void testReadErrors() throws Exception { + String srcPath = "/test0.txt"; + Uri testFile = mStorage.createFile(SRC, srcPath, "text/plain", + "The five boxing wizards jump quickly".getBytes()); + + assertDstFileCountEquals(0); + + mStorage.simulateReadErrors(true); + + copyToDestination(Lists.newArrayList(testFile)); + + // 3 operations: file creation, writing, then deletion (due to failed copy). + mResolver.waitForChanges(3); + + assertDstFileCountEquals(0); + } + /** * Copies the given files to a pre-determined destination. * @@ -200,82 +275,55 @@ public class CopyTest extends ServiceTestCase<CopyService> { assertEquals("Incorrect file count after copy", expected, count); } - /** - * Verifies that the file pointed to by the given URI was correctly copied to the destination. - */ - private void assertCopied(Uri src) throws Exception { - Cursor cursor = null; - String srcName = null; - try { - cursor = mClient.query(src, null, null, null, null); - if (cursor.moveToFirst()) { - srcName = getCursorString(cursor, Document.COLUMN_DISPLAY_NAME); - } - } finally { - IoUtils.closeQuietly(cursor); - } - Uri dst = getDstFileUri(srcName); + private void assertCopied(String path) throws Exception { + File srcFile = mStorage.getFile(SRC, path); + File dstFile = mStorage.getFile(DST, path); + assertNotNull(dstFile); - InputStream in0 = null; - InputStream in1 = null; + FileInputStream src = null; + FileInputStream dst = null; try { - in0 = new ParcelFileDescriptor.AutoCloseInputStream(mClient.openFile(src, "r")); - in1 = new ParcelFileDescriptor.AutoCloseInputStream(mClient.openFile(dst, "r")); - - byte[] buffer0 = Streams.readFully(in0); - byte[] buffer1 = Streams.readFully(in1); - - MoreAsserts.assertEquals(buffer0, buffer1); - } finally { - IoUtils.closeQuietly(in0); - IoUtils.closeQuietly(in1); - } - } + src = new FileInputStream(srcFile); + dst = new FileInputStream(dstFile); + byte[] srcbuf = Streams.readFully(src); + byte[] dstbuf = Streams.readFully(dst); - /** - * Generates a file URI from a given filename. This assumes the file already exists in the - * destination root. - */ - private Uri getDstFileUri(String filename) throws RemoteException { - final Uri dstFileQuery = DocumentsContract.buildChildDocumentsUri(AUTHORITY, - mRoots.get(1).documentId); - Cursor cursor = null; - try { - // StubProvider doesn't seem to support query strings; filter the results manually. - cursor = mClient.query(dstFileQuery, null, null, null, null); - while (cursor.moveToNext()) { - if (filename.equals(getCursorString(cursor, Document.COLUMN_DISPLAY_NAME))) { - return DocumentsContract.buildDocumentUri(AUTHORITY, - getCursorString(cursor, Document.COLUMN_DOCUMENT_ID)); - } - } + MoreAsserts.assertEquals(srcbuf, dstbuf); } finally { - IoUtils.closeQuietly(cursor); + IoUtils.closeQuietly(src); + IoUtils.closeQuietly(dst); } - return null; } /** * Sets up a ContextWrapper that substitutes a stub NotificationManager. This allows the test to * listen for notification events, to gauge copy progress. + * + * @throws FileNotFoundException */ - private void setupTestContext() { - mContext = getSystemContext(); - System.setProperty("dexmaker.dexcache", mContext.getCacheDir().getPath()); + private void setupTestContext() throws FileNotFoundException { + mSystemContext = getSystemContext(); - mNotificationManager = Mockito.spy((NotificationManager) mContext - .getSystemService(Context.NOTIFICATION_SERVICE)); - - // Insert a stub NotificationManager that enables us to listen for when copying is complete. - setContext(new ContextWrapper(mContext) { + // Set up the context with the test content resolver. + mResolver = new TestContentResolver(); + mContext = new ContextWrapper(mSystemContext) { @Override - public Object getSystemService(String name) { - if (Context.NOTIFICATION_SERVICE.equals(name)) { - return mNotificationManager; - } else { - return super.getSystemService(name); - } + public ContentResolver getContentResolver() { + return mResolver; } - }); + }; + setContext(mContext); + + // Create a local stub provider and add it to the content resolver. + ProviderInfo info = new ProviderInfo(); + info.authority = AUTHORITY; + info.exported = true; + info.grantUriPermissions = true; + info.readPermission = android.Manifest.permission.MANAGE_DOCUMENTS; + info.writePermission = android.Manifest.permission.MANAGE_DOCUMENTS; + + mStorage = new StubProvider(); + mStorage.attachInfo(mContext, info); + mResolver.addProvider(AUTHORITY, mStorage); } } diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java b/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java index 438f6cd..8cef433 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java @@ -21,9 +21,10 @@ import android.content.SharedPreferences; import android.content.pm.ProviderInfo; import android.content.res.AssetFileDescriptor; import android.database.Cursor; -import android.database.MatrixCursor.RowBuilder; import android.database.MatrixCursor; +import android.database.MatrixCursor.RowBuilder; import android.graphics.Point; +import android.net.Uri; import android.os.Bundle; import android.os.CancellationSignal; import android.os.FileUtils; @@ -32,15 +33,16 @@ import android.provider.DocumentsContract; import android.provider.DocumentsContract.Document; import android.provider.DocumentsContract.Root; import android.provider.DocumentsProvider; +import android.support.annotation.VisibleForTesting; import android.util.Log; import com.google.android.collect.Maps; import libcore.io.IoUtils; -import java.io.FileOutputStream; import java.io.File; import java.io.FileNotFoundException; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -70,6 +72,7 @@ public class StubProvider extends DocumentsProvider { private String mAuthority; private SharedPreferences mPrefs; private Map<String, RootInfo> mRoots; + private boolean mSimulateReadErrors; @Override public void attachInfo(Context context, ProviderInfo info) { @@ -83,7 +86,8 @@ public class StubProvider extends DocumentsProvider { return true; } - private void clearCacheAndBuildRoots() { + @VisibleForTesting + public void clearCacheAndBuildRoots() { final File cacheDir = getContext().getCacheDir(); removeRecursively(cacheDir); mStorage.clear(); @@ -164,7 +168,7 @@ public class StubProvider extends DocumentsProvider { } else { try { if (!file.createNewFile()) { - throw new FileNotFoundException(); + throw new IllegalStateException("The file " + file.getPath() + " already exists"); } } catch (IOException e) { throw new FileNotFoundException(); @@ -173,6 +177,10 @@ public class StubProvider extends DocumentsProvider { final StubDocument document = new StubDocument(file, mimeType, parentDocument); notifyParentChanged(document.parentId); + getContext().getContentResolver().notifyChange( + DocumentsContract.buildDocumentUri(mAuthority, document.documentId), + null, false); + return document.documentId; } @@ -187,6 +195,9 @@ public class StubProvider extends DocumentsProvider { document.rootInfo.size -= fileSize; } notifyParentChanged(document.parentId); + getContext().getContentResolver().notifyChange( + DocumentsContract.buildDocumentUri(mAuthority, document.documentId), + null, false); } @Override @@ -226,7 +237,17 @@ public class StubProvider extends DocumentsProvider { throw new FileNotFoundException(); if ("r".equals(mode)) { - return ParcelFileDescriptor.open(document.file, ParcelFileDescriptor.MODE_READ_ONLY); + ParcelFileDescriptor pfd = ParcelFileDescriptor.open(document.file, + ParcelFileDescriptor.MODE_READ_ONLY); + if (mSimulateReadErrors) { + pfd = new ParcelFileDescriptor(pfd) { + @Override + public void checkError() throws IOException { + throw new IOException("Test error"); + } + }; + } + return pfd; } if ("w".equals(mode)) { return startWrite(document); @@ -235,6 +256,11 @@ public class StubProvider extends DocumentsProvider { throw new FileNotFoundException(); } + @VisibleForTesting + public void simulateReadErrors(boolean b) { + mSimulateReadErrors = b; + } + @Override public AssetFileDescriptor openDocumentThumbnail( String docId, Point sizeHint, CancellationSignal signal) throws FileNotFoundException { @@ -281,11 +307,15 @@ public class StubProvider extends DocumentsProvider { } } } catch (IOException e) { + Log.e(TAG, "Error on close", e); closePipeWithErrorSilently(readPipe, e.getMessage()); } finally { IoUtils.closeQuietly(inputStream); IoUtils.closeQuietly(outputStream); notifyParentChanged(document.parentId); + getContext().getContentResolver().notifyChange( + DocumentsContract.buildDocumentUri(mAuthority, document.documentId), + null, false); } } }.start(); @@ -302,7 +332,6 @@ public class StubProvider extends DocumentsProvider { @Override public Bundle call(String method, String arg, Bundle extras) { - Log.d(TAG, "call: " + method + arg); switch (method) { case "clear": clearCacheAndBuildRoots(); @@ -376,30 +405,51 @@ public class StubProvider extends DocumentsProvider { } } - public File createFile(String rootId, File parent, String mimeType, String name) - throws IOException { - StubDocument parentDoc = null; + @VisibleForTesting + public Uri createFile(String rootId, String path, String mimeType, byte[] content) + throws FileNotFoundException, IOException { + StubDocument root = mRoots.get(rootId).rootDocument; + if (root == null) { + throw new FileNotFoundException("No roots with the ID " + rootId + " were found"); + } + File file = new File(root.file, path.substring(1)); + StubDocument parent = mStorage.get(getDocumentIdForFile(file.getParentFile())); if (parent == null) { - // Use the root dir as the parent, if one wasn't specified. - parentDoc = mRoots.get(rootId).rootDocument; - } else { - // Verify that the parent exists and is a directory. - parentDoc = mStorage.get(getDocumentIdForFile(parent)); - if (parentDoc == null) { - throw new IllegalArgumentException("Parent file not found."); + parent = mStorage.get(createFile(rootId, file.getParentFile().getPath(), + DocumentsContract.Document.MIME_TYPE_DIR, null)); + } + + if (DocumentsContract.Document.MIME_TYPE_DIR.equals(mimeType)) { + if (!file.mkdirs()) { + throw new FileNotFoundException("Couldn't create directory " + file.getPath()); } - if (!Document.MIME_TYPE_DIR.equals(parentDoc.mimeType)) { - throw new IllegalArgumentException("Parent file must be a directory."); + } else { + if (!file.createNewFile()) { + throw new FileNotFoundException("Couldn't create file " + file.getPath()); } + // Add content to the file. + FileOutputStream fout = new FileOutputStream(file); + fout.write(content); + fout.close(); } - File file = new File(parentDoc.file, name); - if (Document.MIME_TYPE_DIR.equals(mimeType)) { - file.mkdir(); - } else { - file.createNewFile(); + final StubDocument document = new StubDocument(file, mimeType, parent); + return DocumentsContract.buildDocumentUri(mAuthority, document.documentId); + } + + @VisibleForTesting + public File getFile(String rootId, String path) throws FileNotFoundException { + StubDocument root = mRoots.get(rootId).rootDocument; + if (root == null) { + throw new FileNotFoundException("No roots with the ID " + rootId + " were found"); + } + // Convert the path string into a path that's relative to the root. + File needle = new File(root.file, path.substring(1)); + + StubDocument found = mStorage.get(getDocumentIdForFile(needle)); + if (found == null) { + return null; } - new StubDocument(file, mimeType, parentDoc); - return file; + return found.file; } final class RootInfo { diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java index c7b7628..d13d71c 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -58,7 +58,6 @@ import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback; import android.hardware.fingerprint.FingerprintUtils; import android.hardware.fingerprint.FingerprintManager.AuthenticationResult; -import android.service.trust.TrustAgentService; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener; @@ -154,6 +153,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { private SubscriptionManager mSubscriptionManager; private List<SubscriptionInfo> mSubscriptionInfo; private boolean mFingerprintDetectionRunning; + private TrustManager mTrustManager; private final Handler mHandler = new Handler() { @Override @@ -550,7 +550,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { @Override public void onAuthenticationError(int errMsgId, CharSequence errString) { - mHandler.obtainMessage(MSG_FINGERPRINT_ERROR, errMsgId, 0, errString); + mHandler.obtainMessage(MSG_FINGERPRINT_ERROR, errMsgId, 0, errString).sendToTarget(); } }; private CancellationSignal mFingerprintCancelSignal; @@ -784,8 +784,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { e.printStackTrace(); } - TrustManager trustManager = (TrustManager) context.getSystemService(Context.TRUST_SERVICE); - trustManager.registerTrustListener(this); + mTrustManager = (TrustManager) context.getSystemService(Context.TRUST_SERVICE); + mTrustManager.registerTrustListener(this); mFpm = (FingerprintManager) context.getSystemService(Context.FINGERPRINT_SERVICE); updateFingerprintListeningState(); @@ -801,7 +801,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { } private boolean shouldListenForFingerprint() { - return mScreenOn && mKeyguardIsVisible && !mSwitchingUser; + return mScreenOn && mKeyguardIsVisible && !mSwitchingUser + && mTrustManager.hasUserAuthenticatedSinceBoot(ActivityManager.getCurrentUser()); } private void startListeningForFingerprint() { diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index b2c9cd0..4082bf5 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -111,8 +111,10 @@ <string name="bluetooth_profile_pbap_summary">Use for contact sharing</string> <!-- Bluetooth settings. The user-visible string that is used whenever referring to the PAN profile (sharing this device's Internet connection). [CHAR LIMIT=40] --> <string name="bluetooth_profile_pan_nap">Internet connection sharing</string> - <!-- Bluetooth settings. The user-visible string that is used whenever referring to the map profile. --> + <!-- Bluetooth settings. The user-visible string that is used whenever referring to the map profile. --> <string name="bluetooth_profile_map">Message Access</string> + <!-- Bluetooth settings. The user-visible string that is used whenever referring to the SAP profile (sharing SIM card). --> + <string name="bluetooth_profile_sap">SIM Access</string> <!-- Bluetooth settings. Connection options screen. The summary for the A2DP checkbox preference when A2DP is connected. --> <string name="bluetooth_a2dp_profile_summary_connected">Connected to media audio</string> @@ -122,6 +124,8 @@ <string name="bluetooth_opp_profile_summary_connected">Connected to file transfer server</string> <!-- Bluetooth settings. Connection options screen. The summary for the map checkbox preference when map is connected. --> <string name="bluetooth_map_profile_summary_connected">Connected to map</string> + <!-- Bluetooth settings. Connection options screen. The summary for the sap checkbox preference when SAP is connected. --> + <string name="bluetooth_sap_profile_summary_connected">Connected to SAP</string> <!-- Bluetooth settings. Connection options screen. The summary for the OPP checkbox preference when OPP is not connected. --> <string name="bluetooth_opp_profile_summary_not_connected">Not connected to file transfer server</string> <!-- Bluetooth settings. Connection options screen. The summary for the HID checkbox preference when HID is connected. --> @@ -137,6 +141,8 @@ <string name="bluetooth_pan_profile_summary_use_for">Use for Internet access</string> <!-- Bluetooth settings. Connection options screen. The summary for the map checkbox preference that describes how checking it will set the map profile as preferred. --> <string name="bluetooth_map_profile_summary_use_for">Use for map</string> + <!-- Bluetooth settings. Connection options screen. The summary for the sap checkbox preference that describes how checking it will set the sap profile as preferred. --> + <string name="bluetooth_sap_profile_summary_use_for">Use for SIM access</string> <!-- Bluetooth settings. Connection options screen. The summary for the A2DP checkbox preference that describes how checking it will set the A2DP profile as preferred. --> <string name="bluetooth_a2dp_profile_summary_use_for">Use for media audio</string> <!-- Bluetooth settings. Connection options screen. The summary for the headset checkbox preference that describes how checking it will set the headset profile as preferred. --> diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java index dd2368f..7534b8e 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -67,10 +67,6 @@ public final class CachedBluetoothDevice implements Comparable<CachedBluetoothDe private boolean mVisible; - private int mPhonebookPermissionChoice; - - private int mMessagePermissionChoice; - private int mMessageRejectionCount; private final Collection<Callback> mCallbacks = new ArrayList<Callback>(); @@ -556,6 +552,7 @@ public final class CachedBluetoothDevice implements Comparable<CachedBluetoothDe mConnectAfterPairing = false; // cancel auto-connect setPhonebookPermissionChoice(ACCESS_UNKNOWN); setMessagePermissionChoice(ACCESS_UNKNOWN); + setSimPermissionChoice(ACCESS_UNKNOWN); mMessageRejectionCount = 0; saveMessageRejectionCount(); } @@ -732,6 +729,26 @@ public final class CachedBluetoothDevice implements Comparable<CachedBluetoothDe mDevice.setMessageAccessPermission(permission); } + public int getSimPermissionChoice() { + int permission = mDevice.getSimAccessPermission(); + if (permission == BluetoothDevice.ACCESS_ALLOWED) { + return ACCESS_ALLOWED; + } else if (permission == BluetoothDevice.ACCESS_REJECTED) { + return ACCESS_REJECTED; + } + return ACCESS_UNKNOWN; + } + + void setSimPermissionChoice(int permissionChoice) { + int permission = BluetoothDevice.ACCESS_UNKNOWN; + if (permissionChoice == ACCESS_ALLOWED) { + permission = BluetoothDevice.ACCESS_ALLOWED; + } else if (permissionChoice == ACCESS_REJECTED) { + permission = BluetoothDevice.ACCESS_REJECTED; + } + mDevice.setSimAccessPermission(permission); + } + // Migrates data from old data store (in Settings app's shared preferences) to new (in Bluetooth // app's shared preferences). private void migrateMessagePermissionChoice() { diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java new file mode 100644 index 0000000..25c53e6 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.bluetooth; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothClass; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothSap; +import android.bluetooth.BluetoothProfile; +import android.bluetooth.BluetoothUuid; +import android.content.Context; +import android.os.ParcelUuid; +import android.util.Log; + +import com.android.settingslib.R; + +import java.util.ArrayList; +import java.util.List; + +/** + * SapProfile handles Bluetooth SAP profile. + */ +final class SapProfile implements LocalBluetoothProfile { + private static final String TAG = "SapProfile"; + private static boolean V = true; + + private BluetoothSap mService; + private boolean mIsProfileReady; + + private final LocalBluetoothAdapter mLocalAdapter; + private final CachedBluetoothDeviceManager mDeviceManager; + private final LocalBluetoothProfileManager mProfileManager; + + static final ParcelUuid[] UUIDS = { + BluetoothUuid.SAP, + }; + + static final String NAME = "SAP"; + + // Order of this profile in device profiles list + private static final int ORDINAL = 10; + + // These callbacks run on the main thread. + private final class SapServiceListener + implements BluetoothProfile.ServiceListener { + + public void onServiceConnected(int profile, BluetoothProfile proxy) { + if (V) Log.d(TAG,"Bluetooth service connected"); + mService = (BluetoothSap) proxy; + // We just bound to the service, so refresh the UI for any connected SAP devices. + List<BluetoothDevice> deviceList = mService.getConnectedDevices(); + while (!deviceList.isEmpty()) { + BluetoothDevice nextDevice = deviceList.remove(0); + CachedBluetoothDevice device = mDeviceManager.findDevice(nextDevice); + // we may add a new device here, but generally this should not happen + if (device == null) { + Log.w(TAG, "SapProfile found new device: " + nextDevice); + device = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, nextDevice); + } + device.onProfileStateChanged(SapProfile.this, + BluetoothProfile.STATE_CONNECTED); + device.refresh(); + } + + mProfileManager.callServiceConnectedListeners(); + mIsProfileReady=true; + } + + public void onServiceDisconnected(int profile) { + if (V) Log.d(TAG,"Bluetooth service disconnected"); + mProfileManager.callServiceDisconnectedListeners(); + mIsProfileReady=false; + } + } + + public boolean isProfileReady() { + return mIsProfileReady; + } + + SapProfile(Context context, LocalBluetoothAdapter adapter, + CachedBluetoothDeviceManager deviceManager, + LocalBluetoothProfileManager profileManager) { + mLocalAdapter = adapter; + mDeviceManager = deviceManager; + mProfileManager = profileManager; + mLocalAdapter.getProfileProxy(context, new SapServiceListener(), + BluetoothProfile.SAP); + } + + public boolean isConnectable() { + return true; + } + + public boolean isAutoConnectable() { + return true; + } + + public boolean connect(BluetoothDevice device) { + if (mService == null) return false; + List<BluetoothDevice> sinks = mService.getConnectedDevices(); + if (sinks != null) { + for (BluetoothDevice sink : sinks) { + mService.disconnect(sink); + } + } + return mService.connect(device); + } + + public boolean disconnect(BluetoothDevice device) { + if (mService == null) return false; + List<BluetoothDevice> deviceList = mService.getConnectedDevices(); + if (!deviceList.isEmpty() && deviceList.get(0).equals(device)) { + if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) { + mService.setPriority(device, BluetoothProfile.PRIORITY_ON); + } + return mService.disconnect(device); + } else { + return false; + } + } + + public int getConnectionStatus(BluetoothDevice device) { + if (mService == null) return BluetoothProfile.STATE_DISCONNECTED; + List<BluetoothDevice> deviceList = mService.getConnectedDevices(); + + return !deviceList.isEmpty() && deviceList.get(0).equals(device) + ? mService.getConnectionState(device) + : BluetoothProfile.STATE_DISCONNECTED; + } + + public boolean isPreferred(BluetoothDevice device) { + if (mService == null) return false; + return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF; + } + + public int getPreferred(BluetoothDevice device) { + if (mService == null) return BluetoothProfile.PRIORITY_OFF; + return mService.getPriority(device); + } + + public void setPreferred(BluetoothDevice device, boolean preferred) { + if (mService == null) return; + if (preferred) { + if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) { + mService.setPriority(device, BluetoothProfile.PRIORITY_ON); + } + } else { + mService.setPriority(device, BluetoothProfile.PRIORITY_OFF); + } + } + + public List<BluetoothDevice> getConnectedDevices() { + if (mService == null) return new ArrayList<BluetoothDevice>(0); + return mService.getDevicesMatchingConnectionStates( + new int[] {BluetoothProfile.STATE_CONNECTED, + BluetoothProfile.STATE_CONNECTING, + BluetoothProfile.STATE_DISCONNECTING}); + } + + public String toString() { + return NAME; + } + + public int getOrdinal() { + return ORDINAL; + } + + public int getNameResource(BluetoothDevice device) { + return R.string.bluetooth_profile_sap; + } + + public int getSummaryResourceForDevice(BluetoothDevice device) { + int state = getConnectionStatus(device); + switch (state) { + case BluetoothProfile.STATE_DISCONNECTED: + return R.string.bluetooth_sap_profile_summary_use_for; + + case BluetoothProfile.STATE_CONNECTED: + return R.string.bluetooth_sap_profile_summary_connected; + + default: + return Utils.getConnectionStateSummary(state); + } + } + + public int getDrawableResource(BluetoothClass btClass) { + return R.drawable.ic_bt_cellphone; + } + + protected void finalize() { + if (V) Log.d(TAG, "finalize()"); + if (mService != null) { + try { + BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.SAP, + mService); + mService = null; + }catch (Throwable t) { + Log.w(TAG, "Error cleaning up SAP proxy", t); + } + } + } +} diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java index 1cf7248..c81f22a 100644 --- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java +++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java @@ -32,7 +32,6 @@ import android.os.HandlerThread; import android.os.IBinder; import android.os.Looper; import android.os.Message; -import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import android.os.storage.StorageVolume; @@ -349,6 +348,11 @@ public class StorageMeasurement { final Message finished = mMeasurementHandler.obtainMessage(MeasurementHandler.MSG_COMPLETED, details); + if (mVolume == null || !mVolume.isMountedReadable()) { + finished.sendToTarget(); + return; + } + if (mSharedVolume != null && mSharedVolume.isMountedReadable()) { final File basePath = mSharedVolume.getPathForUser(currentUser); @@ -375,8 +379,10 @@ public class StorageMeasurement { } final File file = mVolume.getPath(); - details.totalSize = file.getTotalSpace(); - details.availSize = file.getFreeSpace(); + if (file != null) { + details.totalSize = file.getTotalSpace(); + details.availSize = file.getFreeSpace(); + } // Measure all apps hosted on this volume for all users if (mVolume.getType() == VolumeInfo.TYPE_PRIVATE) { diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index e5a39c8..8466a5a 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -958,6 +958,12 @@ <!-- Button label for ending zen mode in the volume dialog --> <string name="volume_zen_end_now">End now</string> + <!-- Content description for accessibility (not shown on the screen): volume dialog expand button. [CHAR LIMIT=NONE] --> + <string name="accessibility_volume_expand">Expand</string> + + <!-- Content description for accessibility (not shown on the screen): volume dialog collapse button. [CHAR LIMIT=NONE] --> + <string name="accessibility_volume_collapse">Collapse</string> + <!-- Screen pinning dialog title. --> <string name="screen_pinning_title">Screen is pinned</string> <!-- Screen pinning dialog description. --> diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java index 1001feb..ad97f91 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java @@ -39,7 +39,6 @@ import com.android.systemui.R; import com.android.systemui.recents.misc.DebugTrigger; import com.android.systemui.recents.misc.ReferenceCountedTrigger; import com.android.systemui.recents.misc.SystemServicesProxy; -import com.android.systemui.recents.misc.Utilities; import com.android.systemui.recents.model.RecentsTaskLoadPlan; import com.android.systemui.recents.model.RecentsTaskLoader; import com.android.systemui.recents.model.Task; @@ -392,15 +391,6 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView filter.addAction(Intent.ACTION_SCREEN_OFF); filter.addAction(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED); registerReceiver(mSystemBroadcastReceiver, filter); - - // Private API calls to make the shadows look better - try { - Utilities.setShadowProperty("ambientRatio", String.valueOf(1.5f)); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } catch (InvocationTargetException e) { - e.printStackTrace(); - } } /** Inflates the debug overlay if debug mode is enabled. */ diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java index 84544ff..e810410 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java @@ -22,27 +22,11 @@ import android.graphics.Matrix; import android.graphics.Rect; import android.view.View; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.util.ArrayList; /* Common code */ public class Utilities { - // Reflection methods for altering shadows - private static Method sPropertyMethod; - static { - try { - Class<?> c = Class.forName("android.view.DisplayListCanvas"); - sPropertyMethod = c.getDeclaredMethod("setProperty", String.class, String.class); - if (!sPropertyMethod.isAccessible()) sPropertyMethod.setAccessible(true); - } catch (ClassNotFoundException e) { - e.printStackTrace(); - } catch (NoSuchMethodException e) { - e.printStackTrace(); - } - } - /** Scales a rect about its centroid */ public static void scaleRectAboutCenter(Rect r, float scale) { if (scale != 1.0f) { @@ -163,12 +147,6 @@ public class Utilities { (1f - overlayAlpha) * Color.blue(overlayColor))); } - /** Sets some private shadow properties. */ - public static void setShadowProperty(String property, String value) - throws IllegalAccessException, InvocationTargetException { - sPropertyMethod.invoke(null, property, value); - } - /** * Cancels an animation ensuring that if it has listeners, onCancel and onEnd * are not called. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java index f3d4c7f..d5209ea 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java @@ -80,6 +80,14 @@ public class PanelBar extends FrameLayout { } } + public void setBouncerShowing(boolean showing) { + if (mPanelHolder != null) { + mPanelHolder.setImportantForAccessibility( + showing ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS + : IMPORTANT_FOR_ACCESSIBILITY_AUTO); + } + } + public float getBarHeight() { return getMeasuredHeight(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index f3335af..351977b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -76,6 +76,7 @@ import android.view.Display; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; +import android.view.ThreadedRenderer; import android.view.VelocityTracker; import android.view.View; import android.view.ViewGroup.LayoutParams; @@ -859,6 +860,12 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, // listen for USER_SETUP_COMPLETE setting (per-user) resetUserSetupObserver(); + // disable profiling bars, since they overlap and clutter the output on app windows + ThreadedRenderer.overrideProperty("disableProfileBars", "true"); + + // Private API call to make the shadows look better for Recents + ThreadedRenderer.overrideProperty("ambientRatio", String.valueOf(1.5f)); + return mStatusBarView; } @@ -3608,6 +3615,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, @Override public void setBouncerShowing(boolean bouncerShowing) { super.setBouncerShowing(bouncerShowing); + mStatusBarView.setBouncerShowing(bouncerShowing); disable(mDisabledUnmodified1, mDisabledUnmodified2, true /* animate */); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileDataControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileDataControllerImpl.java index f2b2f66..a7fdadc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileDataControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileDataControllerImpl.java @@ -162,7 +162,7 @@ public class MobileDataControllerImpl implements NetworkController.MobileDataCon usage.warningLevel = DEFAULT_WARNING_LEVEL; } if (usage != null) { - usage.carrier = mNetworkController.getMobileNetworkName(); + usage.carrier = mNetworkController.getMobileDataNetworkName(); } return usage; } catch (RemoteException e) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java index c3c6b12..22bf47c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java @@ -84,6 +84,7 @@ public class MobileSignalController extends SignalController< mapIconSets(); mLastState.networkName = mCurrentState.networkName = mNetworkNameDefault; + mLastState.networkNameData = mCurrentState.networkNameData = mNetworkNameDefault; mLastState.enabled = mCurrentState.enabled = hasMobileData; mLastState.iconGroup = mCurrentState.iconGroup = mDefaultIcons; // Get initial data sim state. @@ -294,6 +295,7 @@ public class MobileSignalController extends SignalController< if (action.equals(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION)) { updateNetworkName(intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_SPN, false), intent.getStringExtra(TelephonyIntents.EXTRA_SPN), + intent.getStringExtra(TelephonyIntents.EXTRA_DATA_SPN), intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_PLMN, false), intent.getStringExtra(TelephonyIntents.EXTRA_PLMN)); notifyListenersIfNecessary(); @@ -322,14 +324,18 @@ public class MobileSignalController extends SignalController< /** * Updates the network's name based on incoming spn and plmn. */ - void updateNetworkName(boolean showSpn, String spn, boolean showPlmn, String plmn) { + void updateNetworkName(boolean showSpn, String spn, String dataSpn, + boolean showPlmn, String plmn) { if (CHATTY) { - Log.d("CarrierLabel", "updateNetworkName showSpn=" + showSpn + " spn=" + spn + Log.d("CarrierLabel", "updateNetworkName showSpn=" + showSpn + + " spn=" + spn + " dataSpn=" + dataSpn + " showPlmn=" + showPlmn + " plmn=" + plmn); } StringBuilder str = new StringBuilder(); + StringBuilder strData = new StringBuilder(); if (showPlmn && plmn != null) { str.append(plmn); + strData.append(plmn); } if (showSpn && spn != null) { if (str.length() != 0) { @@ -342,6 +348,17 @@ public class MobileSignalController extends SignalController< } else { mCurrentState.networkName = mNetworkNameDefault; } + if (showSpn && dataSpn != null) { + if (strData.length() != 0) { + strData.append(mNetworkNameSeparator); + } + strData.append(dataSpn); + } + if (strData.length() != 0) { + mCurrentState.networkNameData = strData.toString(); + } else { + mCurrentState.networkNameData = mNetworkNameDefault; + } } /** @@ -492,6 +509,7 @@ public class MobileSignalController extends SignalController< static class MobileState extends SignalController.State { String networkName; + String networkNameData; boolean dataSim; boolean dataConnected; boolean isEmergency; @@ -505,6 +523,7 @@ public class MobileSignalController extends SignalController< MobileState state = (MobileState) s; dataSim = state.dataSim; networkName = state.networkName; + networkNameData = state.networkNameData; dataConnected = state.dataConnected; inetForNetwork = state.inetForNetwork; isEmergency = state.isEmergency; @@ -518,6 +537,7 @@ public class MobileSignalController extends SignalController< builder.append(','); builder.append("dataSim=").append(dataSim).append(','); builder.append("networkName=").append(networkName).append(','); + builder.append("networkNameData=").append(networkNameData).append(','); builder.append("dataConnected=").append(dataConnected).append(','); builder.append("inetForNetwork=").append(inetForNetwork).append(','); builder.append("isEmergency=").append(isEmergency).append(','); @@ -529,6 +549,7 @@ public class MobileSignalController extends SignalController< public boolean equals(Object o) { return super.equals(o) && Objects.equals(((MobileState) o).networkName, networkName) + && Objects.equals(((MobileState) o).networkNameData, networkNameData) && ((MobileState) o).dataSim == dataSim && ((MobileState) o).dataConnected == dataConnected && ((MobileState) o).isEmergency == isEmergency diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java index 6d88535..df133e4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java @@ -246,9 +246,9 @@ public class NetworkControllerImpl extends BroadcastReceiver return mDefaultSignalController; } - public String getMobileNetworkName() { + public String getMobileDataNetworkName() { MobileSignalController controller = getDataController(); - return controller != null ? controller.getState().networkName : ""; + return controller != null ? controller.getState().networkNameData : ""; } public boolean isEmergencyOnly() { diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java index 9434036..7115897 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java @@ -16,6 +16,9 @@ package com.android.systemui.volume; +import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL_MASK; + +import android.accessibilityservice.AccessibilityServiceInfo; import android.animation.LayoutTransition; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; @@ -32,6 +35,7 @@ import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.media.AudioManager; import android.media.AudioSystem; +import android.os.Debug; import android.os.Handler; import android.os.Looper; import android.os.Message; @@ -43,6 +47,8 @@ import android.util.SparseBooleanArray; import android.view.Gravity; import android.view.MotionEvent; import android.view.View; +import android.view.View.AccessibilityDelegate; +import android.view.View.OnAttachStateChangeListener; import android.view.View.OnClickListener; import android.view.View.OnLayoutChangeListener; import android.view.View.OnTouchListener; @@ -50,6 +56,9 @@ import android.view.ViewGroup; import android.view.ViewGroup.MarginLayoutParams; import android.view.Window; import android.view.WindowManager; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; +import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener; import android.view.animation.DecelerateInterpolator; import android.widget.ImageButton; import android.widget.LinearLayout; @@ -97,6 +106,7 @@ public class VolumeDialog { private final ZenFooter mZenFooter; private final LayoutTransition mLayoutTransition; private final Object mSafetyWarningLock = new Object(); + private final Accessibility mAccessibility = new Accessibility(); private boolean mShowing; private boolean mExpanded; @@ -174,6 +184,8 @@ public class VolumeDialog { mZenFooter = (ZenFooter) mDialog.findViewById(R.id.volume_zen_footer); mZenFooter.init(zenModeController); + mAccessibility.init(); + controller.addCallback(mControllerCallbackH, mHandler); controller.getState(); } @@ -409,10 +421,13 @@ public class VolumeDialog { protected void rescheduleTimeoutH() { mHandler.removeMessages(H.DISMISS); - final int timeout = computeTimeoutH(); - if (D.BUG) Log.d(TAG, "rescheduleTimeout " + timeout); - mHandler.sendMessageDelayed(mHandler - .obtainMessage(H.DISMISS, Events.DISMISS_REASON_TIMEOUT, 0), timeout); + int timeout = -1; + if (!mAccessibility.mFeedbackEnabled) { + timeout = computeTimeoutH(); + mHandler.sendMessageDelayed(mHandler + .obtainMessage(H.DISMISS, Events.DISMISS_REASON_TIMEOUT, 0), timeout); + } + if (D.BUG) Log.d(TAG, "rescheduleTimeout " + timeout + " " + Debug.getCaller()); mController.userActivity(); } @@ -475,6 +490,8 @@ public class VolumeDialog { if (res == mExpandButtonRes) return; mExpandButtonRes = res; mExpandButton.setImageResource(res); + mExpandButton.setContentDescription(mContext.getString(mExpanded ? + R.string.accessibility_volume_collapse : R.string.accessibility_volume_expand)); } private boolean isVisibleH(VolumeRow row, boolean isActive) { @@ -632,6 +649,7 @@ public class VolumeDialog { : (iconRes == R.drawable.ic_volume_media_bt || iconRes == row.iconRes) ? Events.ICON_STATE_UNMUTE : Events.ICON_STATE_UNKNOWN; + row.icon.setContentDescription(ss.name); // update slider updateVolumeRowSliderH(row, zenMuted); @@ -931,6 +949,56 @@ public class VolumeDialog { } } + private final class Accessibility { + private AccessibilityManager mMgr; + private boolean mFeedbackEnabled; + + public void init() { + mMgr = (AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE); + mDialogView.addOnAttachStateChangeListener(new OnAttachStateChangeListener() { + @Override + public void onViewDetachedFromWindow(View v) { + // noop + } + + @Override + public void onViewAttachedToWindow(View v) { + updateFeedbackEnabled(); + } + }); + mDialogView.setAccessibilityDelegate(new AccessibilityDelegate() { + @Override + public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child, + AccessibilityEvent event) { + rescheduleTimeoutH(); + return super.onRequestSendAccessibilityEvent(host, child, event); + } + }); + mMgr.addAccessibilityStateChangeListener(new AccessibilityStateChangeListener() { + @Override + public void onAccessibilityStateChanged(boolean enabled) { + updateFeedbackEnabled(); + } + }); + updateFeedbackEnabled(); + } + + private void updateFeedbackEnabled() { + mFeedbackEnabled = computeFeedbackEnabled(); + } + + private boolean computeFeedbackEnabled() { + final List<AccessibilityServiceInfo> services = + mMgr.getEnabledAccessibilityServiceList(FEEDBACK_ALL_MASK); + for (AccessibilityServiceInfo asi : services) { + if ((asi.feedbackType & FEEDBACK_ALL_MASK) != 0) { + return true; + } + } + return false; + } + } + private static class VolumeRow { private View view; private View space; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java index a176c98..29f461e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java @@ -306,6 +306,6 @@ public class NetworkControllerBaseTest extends SysuiTestCase { } protected void assertNetworkNameEquals(String expected) { - assertEquals("Network name", expected, mNetworkController.getMobileNetworkName()); + assertEquals("Network name", expected, mNetworkController.getMobileDataNetworkName()); } } diff --git a/rs/java/android/renderscript/RenderScript.java b/rs/java/android/renderscript/RenderScript.java index e7f210b..4f10699 100644 --- a/rs/java/android/renderscript/RenderScript.java +++ b/rs/java/android/renderscript/RenderScript.java @@ -302,8 +302,12 @@ public class RenderScript { long[] fieldIDs, long[] values, int[] sizes, long[] depClosures, long[] depFieldIDs) { validate(); - return rsnClosureCreate(mContext, kernelID, returnValue, fieldIDs, values, + long c = rsnClosureCreate(mContext, kernelID, returnValue, fieldIDs, values, sizes, depClosures, depFieldIDs); + if (c == 0) { + throw new RSRuntimeException("Failed creating closure."); + } + return c; } native long rsnInvokeClosureCreate(long con, long invokeID, byte[] params, @@ -311,8 +315,12 @@ public class RenderScript { synchronized long nInvokeClosureCreate(long invokeID, byte[] params, long[] fieldIDs, long[] values, int[] sizes) { validate(); - return rsnInvokeClosureCreate(mContext, invokeID, params, fieldIDs, + long c = rsnInvokeClosureCreate(mContext, invokeID, params, fieldIDs, values, sizes); + if (c == 0) { + throw new RSRuntimeException("Failed creating closure."); + } + return c; } native void rsnClosureSetArg(long con, long closureID, int index, @@ -337,7 +345,11 @@ public class RenderScript { synchronized long nScriptGroup2Create(String name, String cachePath, long[] closures) { validate(); - return rsnScriptGroup2Create(mContext, name, cachePath, closures); + long g = rsnScriptGroup2Create(mContext, name, cachePath, closures); + if (g == 0) { + throw new RSRuntimeException("Failed creating script group."); + } + return g; } native void rsnScriptGroup2Execute(long con, long groupID); diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp index 5ef807d..80d6515 100644 --- a/rs/jni/android_renderscript_RenderScript.cpp +++ b/rs/jni/android_renderscript_RenderScript.cpp @@ -43,6 +43,9 @@ //#define LOG_API ALOGE static constexpr bool kLogApi = false; +static constexpr size_t kMaxNumberArgsAndBindings = 1000; +static constexpr size_t kMaxNumberClosuresInScriptGroup = 1000000; +static constexpr size_t kMaxNumberKernelArguments = 256; using namespace android; @@ -328,79 +331,167 @@ nClosureCreate(JNIEnv *_env, jobject _this, jlong con, jlong kernelID, jlong returnValue, jlongArray fieldIDArray, jlongArray valueArray, jintArray sizeArray, jlongArray depClosureArray, jlongArray depFieldIDArray) { + jlong ret = 0; + jlong* jFieldIDs = _env->GetLongArrayElements(fieldIDArray, nullptr); jsize fieldIDs_length = _env->GetArrayLength(fieldIDArray); - RsScriptFieldID* fieldIDs = - (RsScriptFieldID*)alloca(sizeof(RsScriptFieldID) * fieldIDs_length); - for (int i = 0; i< fieldIDs_length; i++) { + jlong* jValues = _env->GetLongArrayElements(valueArray, nullptr); + jsize values_length = _env->GetArrayLength(valueArray); + jint* jSizes = _env->GetIntArrayElements(sizeArray, nullptr); + jsize sizes_length = _env->GetArrayLength(sizeArray); + jlong* jDepClosures = + _env->GetLongArrayElements(depClosureArray, nullptr); + jsize depClosures_length = _env->GetArrayLength(depClosureArray); + jlong* jDepFieldIDs = + _env->GetLongArrayElements(depFieldIDArray, nullptr); + jsize depFieldIDs_length = _env->GetArrayLength(depFieldIDArray); + + size_t numValues, numDependencies; + RsScriptFieldID* fieldIDs; + uintptr_t* values; + RsClosure* depClosures; + RsScriptFieldID* depFieldIDs; + + if (fieldIDs_length != values_length || values_length != sizes_length) { + ALOGE("Unmatched field IDs, values, and sizes in closure creation."); + goto exit; + } + + numValues = (size_t)fieldIDs_length; + + if (depClosures_length != depFieldIDs_length) { + ALOGE("Unmatched closures and field IDs for dependencies in closure creation."); + goto exit; + } + + numDependencies = (size_t)depClosures_length; + + if (numDependencies > numValues) { + ALOGE("Unexpected number of dependencies in closure creation"); + goto exit; + } + + if (numValues > kMaxNumberArgsAndBindings) { + ALOGE("Too many arguments or globals in closure creation"); + goto exit; + } + + fieldIDs = (RsScriptFieldID*)alloca(sizeof(RsScriptFieldID) * numValues); + if (fieldIDs == nullptr) { + goto exit; + } + + for (size_t i = 0; i < numValues; i++) { fieldIDs[i] = (RsScriptFieldID)jFieldIDs[i]; } - jlong* jValues = _env->GetLongArrayElements(valueArray, nullptr); - jsize values_length = _env->GetArrayLength(valueArray); - uintptr_t* values = (uintptr_t*)alloca(sizeof(uintptr_t) * values_length); - for (int i = 0; i < values_length; i++) { + values = (uintptr_t*)alloca(sizeof(uintptr_t) * numValues); + if (values == nullptr) { + goto exit; + } + + for (size_t i = 0; i < numValues; i++) { values[i] = (uintptr_t)jValues[i]; } - jint* sizes = _env->GetIntArrayElements(sizeArray, nullptr); - jsize sizes_length = _env->GetArrayLength(sizeArray); + depClosures = (RsClosure*)alloca(sizeof(RsClosure) * numDependencies); + if (depClosures == nullptr) { + goto exit; + } - jlong* jDepClosures = - _env->GetLongArrayElements(depClosureArray, nullptr); - jsize depClosures_length = _env->GetArrayLength(depClosureArray); - RsClosure* depClosures = - (RsClosure*)alloca(sizeof(RsClosure) * depClosures_length); - for (int i = 0; i < depClosures_length; i++) { + for (size_t i = 0; i < numDependencies; i++) { depClosures[i] = (RsClosure)jDepClosures[i]; } - jlong* jDepFieldIDs = - _env->GetLongArrayElements(depFieldIDArray, nullptr); - jsize depFieldIDs_length = _env->GetArrayLength(depFieldIDArray); - RsScriptFieldID* depFieldIDs = - (RsScriptFieldID*)alloca(sizeof(RsScriptFieldID) * depFieldIDs_length); - for (int i = 0; i < depClosures_length; i++) { + depFieldIDs = (RsScriptFieldID*)alloca(sizeof(RsScriptFieldID) * numDependencies); + if (depFieldIDs == nullptr) { + goto exit; + } + + for (size_t i = 0; i < numDependencies; i++) { depFieldIDs[i] = (RsClosure)jDepFieldIDs[i]; } - return (jlong)(uintptr_t)rsClosureCreate( + ret = (jlong)(uintptr_t)rsClosureCreate( (RsContext)con, (RsScriptKernelID)kernelID, (RsAllocation)returnValue, - fieldIDs, (size_t)fieldIDs_length, values, (size_t)values_length, - (int*)sizes, (size_t)sizes_length, - depClosures, (size_t)depClosures_length, - depFieldIDs, (size_t)depFieldIDs_length); + fieldIDs, numValues, values, numValues, + (int*)jSizes, numValues, + depClosures, numDependencies, + depFieldIDs, numDependencies); + +exit: + + _env->ReleaseLongArrayElements(depFieldIDArray, jDepFieldIDs, JNI_ABORT); + _env->ReleaseLongArrayElements(depClosureArray, jDepClosures, JNI_ABORT); + _env->ReleaseIntArrayElements (sizeArray, jSizes, JNI_ABORT); + _env->ReleaseLongArrayElements(valueArray, jValues, JNI_ABORT); + _env->ReleaseLongArrayElements(fieldIDArray, jFieldIDs, JNI_ABORT); + + return ret; } static jlong nInvokeClosureCreate(JNIEnv *_env, jobject _this, jlong con, jlong invokeID, jbyteArray paramArray, jlongArray fieldIDArray, jlongArray valueArray, jintArray sizeArray) { + jlong ret = 0; + jbyte* jParams = _env->GetByteArrayElements(paramArray, nullptr); jsize jParamLength = _env->GetArrayLength(paramArray); - jlong* jFieldIDs = _env->GetLongArrayElements(fieldIDArray, nullptr); jsize fieldIDs_length = _env->GetArrayLength(fieldIDArray); - RsScriptFieldID* fieldIDs = - (RsScriptFieldID*)alloca(sizeof(RsScriptFieldID) * fieldIDs_length); - for (int i = 0; i< fieldIDs_length; i++) { + jlong* jValues = _env->GetLongArrayElements(valueArray, nullptr); + jsize values_length = _env->GetArrayLength(valueArray); + jint* jSizes = _env->GetIntArrayElements(sizeArray, nullptr); + jsize sizes_length = _env->GetArrayLength(sizeArray); + + size_t numValues; + RsScriptFieldID* fieldIDs; + uintptr_t* values; + + if (fieldIDs_length != values_length || values_length != sizes_length) { + ALOGE("Unmatched field IDs, values, and sizes in closure creation."); + goto exit; + } + + numValues = (size_t) fieldIDs_length; + + if (numValues > kMaxNumberArgsAndBindings) { + ALOGE("Too many arguments or globals in closure creation"); + goto exit; + } + + fieldIDs = (RsScriptFieldID*)alloca(sizeof(RsScriptFieldID) * numValues); + if (fieldIDs == nullptr) { + goto exit; + } + + for (size_t i = 0; i< numValues; i++) { fieldIDs[i] = (RsScriptFieldID)jFieldIDs[i]; } - jlong* jValues = _env->GetLongArrayElements(valueArray, nullptr); - jsize values_length = _env->GetArrayLength(valueArray); - uintptr_t* values = (uintptr_t*)alloca(sizeof(uintptr_t) * values_length); - for (int i = 0; i < values_length; i++) { - values[i] = (uintptr_t)jValues[i]; + values = (uintptr_t*)alloca(sizeof(uintptr_t) * numValues); + if (values == nullptr) { + goto exit; } - jint* sizes = _env->GetIntArrayElements(sizeArray, nullptr); - jsize sizes_length = _env->GetArrayLength(sizeArray); + for (size_t i = 0; i < numValues; i++) { + values[i] = (uintptr_t)jValues[i]; + } - return (jlong)(uintptr_t)rsInvokeClosureCreate( + ret = (jlong)(uintptr_t)rsInvokeClosureCreate( (RsContext)con, (RsScriptInvokeID)invokeID, jParams, jParamLength, - fieldIDs, (size_t)fieldIDs_length, values, (size_t)values_length, - (int*)sizes, (size_t)sizes_length); + fieldIDs, numValues, values, numValues, + (int*)jSizes, numValues); + +exit: + + _env->ReleaseIntArrayElements (sizeArray, jSizes, JNI_ABORT); + _env->ReleaseLongArrayElements(valueArray, jValues, JNI_ABORT); + _env->ReleaseLongArrayElements(fieldIDArray, jFieldIDs, JNI_ABORT); + _env->ReleaseByteArrayElements(paramArray, jParams, JNI_ABORT); + + return ret; } static void @@ -420,20 +511,40 @@ nClosureSetGlobal(JNIEnv *_env, jobject _this, jlong con, jlong closureID, static long nScriptGroup2Create(JNIEnv *_env, jobject _this, jlong con, jstring name, jstring cacheDir, jlongArray closureArray) { + jlong ret = 0; + AutoJavaStringToUTF8 nameUTF(_env, name); AutoJavaStringToUTF8 cacheDirUTF(_env, cacheDir); jlong* jClosures = _env->GetLongArrayElements(closureArray, nullptr); jsize numClosures = _env->GetArrayLength(closureArray); - RsClosure* closures = (RsClosure*)alloca(sizeof(RsClosure) * numClosures); + + RsClosure* closures; + + if (numClosures > (jsize) kMaxNumberClosuresInScriptGroup) { + ALOGE("Too many closures in script group"); + goto exit; + } + + closures = (RsClosure*)alloca(sizeof(RsClosure) * numClosures); + if (closures == nullptr) { + goto exit; + } + for (int i = 0; i < numClosures; i++) { closures[i] = (RsClosure)jClosures[i]; } - return (jlong)(uintptr_t)rsScriptGroup2Create( + ret = (jlong)(uintptr_t)rsScriptGroup2Create( (RsContext)con, nameUTF.c_str(), nameUTF.length(), cacheDirUTF.c_str(), cacheDirUTF.length(), closures, numClosures); + +exit: + + _env->ReleaseLongArrayElements(closureArray, jClosures, JNI_ABORT); + + return ret; } static void @@ -1756,8 +1867,14 @@ nScriptForEach(JNIEnv *_env, jobject _this, jlong con, jlong script, jint slot, if (ains != nullptr) { in_len = _env->GetArrayLength(ains); - in_ptr = _env->GetLongArrayElements(ains, nullptr); + if (in_len > (jint)kMaxNumberKernelArguments) { + ALOGE("Too many arguments in kernel launch."); + // TODO (b/20758983): Report back to Java and throw an exception + return; + } + // TODO (b/20760800): Check in_ptr is not null + in_ptr = _env->GetLongArrayElements(ains, nullptr); if (sizeof(RsAllocation) == sizeof(jlong)) { in_allocs = (RsAllocation*)in_ptr; @@ -1765,6 +1882,11 @@ nScriptForEach(JNIEnv *_env, jobject _this, jlong con, jlong script, jint slot, // Convert from 64-bit jlong types to the native pointer type. in_allocs = (RsAllocation*)alloca(in_len * sizeof(RsAllocation)); + if (in_allocs == nullptr) { + ALOGE("Failed launching kernel for lack of memory."); + _env->ReleaseLongArrayElements(ains, in_ptr, JNI_ABORT); + return; + } for (int index = in_len; --index >= 0;) { in_allocs[index] = (RsAllocation)in_ptr[index]; diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java index 4fda370..45909db 100644 --- a/services/core/java/com/android/server/InputMethodManagerService.java +++ b/services/core/java/com/android/server/InputMethodManagerService.java @@ -145,10 +145,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub private static final char INPUT_METHOD_SEPARATOR = ':'; private static final char INPUT_METHOD_SUBTYPE_SEPARATOR = ';'; - static final int MSG_SHOW_IM_PICKER = 1; - static final int MSG_SHOW_IM_SUBTYPE_PICKER = 2; - static final int MSG_SHOW_IM_SUBTYPE_ENABLER = 3; - static final int MSG_SHOW_IM_CONFIG = 4; + static final int MSG_SHOW_IM_SUBTYPE_PICKER = 1; + static final int MSG_SHOW_IM_SUBTYPE_ENABLER = 2; + static final int MSG_SHOW_IM_CONFIG = 3; static final int MSG_UNBIND_INPUT = 1000; static final int MSG_BIND_INPUT = 1010; @@ -2597,12 +2596,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub public boolean handleMessage(Message msg) { SomeArgs args; switch (msg.what) { - case MSG_SHOW_IM_PICKER: - showInputMethodMenu(); - return true; - case MSG_SHOW_IM_SUBTYPE_PICKER: - showInputMethodSubtypeMenu(); + showInputMethodMenu(); return true; case MSG_SHOW_IM_SUBTYPE_ENABLER: @@ -2861,14 +2856,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // ---------------------------------------------------------------------- - private void showInputMethodMenu() { - showInputMethodMenuInternal(false); - } - - private void showInputMethodSubtypeMenu() { - showInputMethodMenuInternal(true); - } - private void showInputMethodAndSubtypeEnabler(String inputMethodId) { Intent intent = new Intent(Settings.ACTION_INPUT_METHOD_SUBTYPE_SETTINGS); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK @@ -2893,7 +2880,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub && mKeyguardManager.isKeyguardLocked() && mKeyguardManager.isKeyguardSecure(); } - private void showInputMethodMenuInternal(boolean showSubtypes) { + private void showInputMethodMenu() { if (DEBUG) Slog.v(TAG, "Show switching menu"); final Context context = mContext; @@ -2915,7 +2902,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub final List<ImeSubtypeListItem> imList = mSwitchingController.getSortedInputMethodAndSubtypeListLocked( - showSubtypes, mInputShown, isScreenLocked); + true /* showSubtypes */, mInputShown, isScreenLocked); if (lastInputMethodSubtypeId == NOT_A_SUBTYPE_ID) { final InputMethodSubtype currentSubtype = getCurrentInputMethodSubtypeLocked(); @@ -3016,7 +3003,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub }; mDialogBuilder.setSingleChoiceItems(adapter, checkedItem, choiceListener); - if (showSubtypes && !isScreenLocked) { + if (!isScreenLocked) { final OnClickListener positiveListener = new OnClickListener() { @Override public void onClick(DialogInterface dialog, int whichButton) { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index eea6234..82dbfee 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -414,6 +414,11 @@ public final class ActivityManagerService extends ActivityManagerNative ActivityRecord mFocusedActivity = null; /** + * User id of the last activity mFocusedActivity was set to. + */ + private int mLastFocusedUserId; + + /** * List of intents that were used to start the most recent tasks. */ private final RecentTasks mRecentTasks; @@ -2561,19 +2566,32 @@ public final class ActivityManagerService extends ActivityManagerNative mWindowManager.setFocusedApp(r.appToken, true); } applyUpdateLockStateLocked(r); - if (last != null && last.userId != mFocusedActivity.userId) { + if (mFocusedActivity.userId != mLastFocusedUserId) { mHandler.removeMessages(FOREGROUND_PROFILE_CHANGED_MSG); mHandler.sendMessage(mHandler.obtainMessage(FOREGROUND_PROFILE_CHANGED_MSG, mFocusedActivity.userId, 0)); + mLastFocusedUserId = mFocusedActivity.userId; } } - EventLog.writeEvent(EventLogTags.AM_FOCUSED_ACTIVITY, mCurrentUserId, + EventLog.writeEvent(EventLogTags.AM_FOCUSED_ACTIVITY, + mFocusedActivity == null ? -1 : mFocusedActivity.userId, mFocusedActivity == null ? "NULL" : mFocusedActivity.shortComponentName); } final void clearFocusedActivity(ActivityRecord r) { if (mFocusedActivity == r) { + ActivityStack stack = mStackSupervisor.getFocusedStack(); + if (stack != null) { + ActivityRecord top = stack.topActivity(); + if (top != null && top.userId != mLastFocusedUserId) { + mHandler.removeMessages(FOREGROUND_PROFILE_CHANGED_MSG); + mHandler.sendMessage(mHandler.obtainMessage(FOREGROUND_PROFILE_CHANGED_MSG, + top.userId, 0)); + mLastFocusedUserId = top.userId; + } + } mFocusedActivity = null; + EventLog.writeEvent(EventLogTags.AM_FOCUSED_ACTIVITY, -1, "NULL"); } } @@ -10754,9 +10772,7 @@ public final class ActivityManagerService extends ActivityManagerNative @Override public void killUid(int uid, String reason) { - if (Binder.getCallingUid() != Process.SYSTEM_UID) { - throw new SecurityException("killUid only available to the system"); - } + enforceCallingPermission(Manifest.permission.KILL_UID, "killUid"); synchronized (this) { killPackageProcessesLocked(null, UserHandle.getAppId(uid), UserHandle.getUserId(uid), ProcessList.FOREGROUND_APP_ADJ-1, false, true, true, false, diff --git a/services/core/java/com/android/server/notification/CalendarTracker.java b/services/core/java/com/android/server/notification/CalendarTracker.java index 8734d97..c82df48 100644 --- a/services/core/java/com/android/server/notification/CalendarTracker.java +++ b/services/core/java/com/android/server/notification/CalendarTracker.java @@ -55,7 +55,6 @@ public class CalendarTracker { Attendees.EVENT_ID, Attendees.ATTENDEE_EMAIL, Attendees.ATTENDEE_STATUS, - Attendees.ATTENDEE_TYPE, }; private static final String ATTENDEE_SELECTION = Attendees.EVENT_ID + " = ? AND " @@ -191,16 +190,13 @@ public class CalendarTracker { final long rowEventId = cursor.getLong(0); final String rowEmail = cursor.getString(1); final int status = cursor.getInt(2); - final int type = cursor.getInt(3); final boolean meetsReply = meetsReply(filter.reply, status); - final boolean meetsAttendance = meetsAttendance(filter.attendance, type); if (DEBUG) Log.d(TAG, (DEBUG_ATTENDEES ? String.format( "rowEventId=%s, rowEmail=%s, ", rowEventId, rowEmail) : "") + - String.format("status=%s, type=%s, meetsReply=%s, meetsAttendance=%s", - attendeeStatusToString(status), attendeeTypeToString(type), meetsReply, - meetsAttendance)); + String.format("status=%s, meetsReply=%s", + attendeeStatusToString(status), meetsReply)); final boolean eventMeets = rowEventId == eventId && Objects.equals(rowEmail, email) - && meetsReply && meetsAttendance; + && meetsReply; rt |= eventMeets; } return rt; @@ -232,35 +228,17 @@ public class CalendarTracker { } } - private static String attendeeTypeToString(int type) { - switch (type) { - case Attendees.TYPE_NONE: return "TYPE_NONE"; - case Attendees.TYPE_REQUIRED: return "TYPE_REQUIRED"; - case Attendees.TYPE_OPTIONAL: return "TYPE_OPTIONAL"; - case Attendees.TYPE_RESOURCE: return "TYPE_RESOURCE"; - default: return "TYPE_" + type; - } - } - - private static boolean meetsAttendance(int attendance, int attendeeType) { - switch (attendance) { - case EventInfo.ATTENDANCE_OPTIONAL: - return attendeeType == Attendees.TYPE_OPTIONAL; - case EventInfo.ATTENDANCE_REQUIRED: - return attendeeType == Attendees.TYPE_REQUIRED; - default: // EventInfo.ATTENDANCE_REQUIRED_OR_OPTIONAL - return true; - } - } - private static boolean meetsReply(int reply, int attendeeStatus) { switch (reply) { case EventInfo.REPLY_YES: return attendeeStatus == Attendees.ATTENDEE_STATUS_ACCEPTED; + case EventInfo.REPLY_YES_OR_MAYBE: + return attendeeStatus == Attendees.ATTENDEE_STATUS_ACCEPTED + || attendeeStatus == Attendees.ATTENDEE_STATUS_TENTATIVE; case EventInfo.REPLY_ANY_EXCEPT_NO: return attendeeStatus != Attendees.ATTENDEE_STATUS_DECLINED; - default: // EventInfo.REPLY_ANY - return true; + default: + return false; } } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 423c732..f30a5674 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -277,8 +277,6 @@ public class PackageManagerService extends IPackageManager.Stub { private static final boolean DEBUG_DEXOPT = false; private static final boolean DEBUG_ABI_SELECTION = false; - static final boolean RUNTIME_PERMISSIONS_ENABLED = true; - private static final int RADIO_UID = Process.PHONE_UID; private static final int LOG_UID = Process.LOG_UID; private static final int NFC_UID = Process.NFC_UID; @@ -2131,13 +2129,6 @@ public class PackageManagerService extends IPackageManager.Stub { + "; regranting permissions for internal storage"); mSettings.mInternalSdkPlatform = mSdkVersion; - // For now runtime permissions are toggled via a system property. - if (!RUNTIME_PERMISSIONS_ENABLED) { - // Remove the runtime permissions state if the feature - // was disabled by flipping the system property. - mSettings.deleteRuntimePermissionsFiles(); - } - updatePermissionsLPw(null, null, UPDATE_PERMISSIONS_ALL | (regrantPermissions ? (UPDATE_PERMISSIONS_REPLACE_PKG|UPDATE_PERMISSIONS_REPLACE_ALL) @@ -3149,13 +3140,9 @@ public class PackageManagerService extends IPackageManager.Stub { } @Override - public boolean grantPermission(String packageName, String name, int userId) { - if (!RUNTIME_PERMISSIONS_ENABLED) { - return false; - } - + public void grantPermission(String packageName, String name, int userId) { if (!sUserManager.exists(userId)) { - return false; + return; } mContext.enforceCallingOrSelfPermission( @@ -3191,12 +3178,13 @@ public class PackageManagerService extends IPackageManager.Stub { final int result = permissionsState.grantRuntimePermission(bp, userId); switch (result) { case PermissionsState.PERMISSION_OPERATION_FAILURE: { - return false; + return; } case PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED: { gidsChanged = true; - } break; + } + break; } // Not critical if that is lost - app has to request again. @@ -3206,18 +3194,12 @@ public class PackageManagerService extends IPackageManager.Stub { if (gidsChanged) { killSettingPackagesForUser(sb, userId, KILL_APP_REASON_GIDS_CHANGED); } - - return true; } @Override - public boolean revokePermission(String packageName, String name, int userId) { - if (!RUNTIME_PERMISSIONS_ENABLED) { - return false; - } - + public void revokePermission(String packageName, String name, int userId) { if (!sUserManager.exists(userId)) { - return false; + return; } mContext.enforceCallingOrSelfPermission( @@ -3251,7 +3233,7 @@ public class PackageManagerService extends IPackageManager.Stub { if (permissionsState.revokeRuntimePermission(bp, userId) == PermissionsState.PERMISSION_OPERATION_FAILURE) { - return false; + return; } // Critical, after this call all should never have the permission. @@ -3259,8 +3241,6 @@ public class PackageManagerService extends IPackageManager.Stub { } killSettingPackagesForUser(sb, userId, KILL_APP_REASON_PERMISSIONS_REVOKED); - - return true; } @Override @@ -7604,9 +7584,7 @@ public class PackageManagerService extends IPackageManager.Stub { } break; case PermissionInfo.PROTECTION_DANGEROUS: { - if (!RUNTIME_PERMISSIONS_ENABLED - || pkg.applicationInfo.targetSdkVersion - <= Build.VERSION_CODES.LOLLIPOP_MR1) { + if (pkg.applicationInfo.targetSdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1) { // For legacy apps dangerous permissions are install time ones. grant = GRANT_INSTALL; } else if (ps.isSystem()) { @@ -7745,10 +7723,8 @@ public class PackageManagerService extends IPackageManager.Stub { ps.setPermissionsUpdatedForUserIds(currentUserIds); // Persist the runtime permissions state for users with changes. - if (RUNTIME_PERMISSIONS_ENABLED) { - for (int userId : changedRuntimePermissionUserIds) { - mSettings.writeRuntimePermissionsForUserLPr(userId, true); - } + for (int userId : changedRuntimePermissionUserIds) { + mSettings.writeRuntimePermissionsForUserLPr(userId, true); } } diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index d3bfdeb..d476bfde 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -4165,19 +4165,11 @@ final class Settings { } public void writePermissionsForUserSyncLPr(int userId) { - if (!PackageManagerService.RUNTIME_PERMISSIONS_ENABLED) { - return; - } - mHandler.removeMessages(userId); writePermissionsSync(userId); } public void writePermissionsForUserAsyncLPr(int userId) { - if (!PackageManagerService.RUNTIME_PERMISSIONS_ENABLED) { - return; - } - final long currentTimeMillis = SystemClock.uptimeMillis(); if (mWriteScheduled.get(userId)) { diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java index 7d2fb43..726db4e 100644 --- a/services/core/java/com/android/server/trust/TrustManagerService.java +++ b/services/core/java/com/android/server/trust/TrustManagerService.java @@ -227,7 +227,7 @@ public class TrustManagerService extends SystemService { if (!userInfo.supportsSwitchTo()) continue; if (!mActivityManager.isUserRunning(userInfo.id)) continue; if (!lockPatternUtils.isSecure(userInfo.id)) continue; - if (!mUserHasAuthenticatedSinceBoot.get(userInfo.id)) continue; + if (!getUserHasAuthenticated(userInfo.id)) continue; DevicePolicyManager dpm = lockPatternUtils.getDevicePolicyManager(); int disabledFeatures = dpm.getKeyguardDisabledFeatures(null, userInfo.id); final boolean disableTrustAgents = @@ -506,7 +506,7 @@ public class TrustManagerService extends SystemService { // Agent dispatch and aggregation private boolean aggregateIsTrusted(int userId) { - if (!mUserHasAuthenticatedSinceBoot.get(userId)) { + if (!getUserHasAuthenticated(userId)) { return false; } for (int i = 0; i < mActiveAgents.size(); i++) { @@ -521,7 +521,7 @@ public class TrustManagerService extends SystemService { } private boolean aggregateIsTrustManaged(int userId) { - if (!mUserHasAuthenticatedSinceBoot.get(userId)) { + if (!getUserHasAuthenticated(userId)) { return false; } for (int i = 0; i < mActiveAgents.size(); i++) { @@ -549,23 +549,46 @@ public class TrustManagerService extends SystemService { } private void updateUserHasAuthenticated(int userId) { - if (!mUserHasAuthenticatedSinceBoot.get(userId)) { - mUserHasAuthenticatedSinceBoot.put(userId, true); + boolean changed = setUserHasAuthenticated(userId); + if (changed) { refreshAgentList(userId); } } + private boolean getUserHasAuthenticated(int userId) { + synchronized (mUserHasAuthenticatedSinceBoot) { + return mUserHasAuthenticatedSinceBoot.get(userId); + } + } - private void requireCredentialEntry(int userId) { - if (userId == UserHandle.USER_ALL) { - mUserHasAuthenticatedSinceBoot.clear(); - refreshAgentList(UserHandle.USER_ALL); - } else { - mUserHasAuthenticatedSinceBoot.put(userId, false); - refreshAgentList(userId); + /** + * @return whether the value has changed + */ + private boolean setUserHasAuthenticated(int userId) { + synchronized (mUserHasAuthenticatedSinceBoot) { + if (!mUserHasAuthenticatedSinceBoot.get(userId)) { + mUserHasAuthenticatedSinceBoot.put(userId, true); + return true; + } + return false; } } + private void clearUserHasAuthenticated(int userId) { + synchronized (mUserHasAuthenticatedSinceBoot) { + if (userId == UserHandle.USER_ALL) { + mUserHasAuthenticatedSinceBoot.clear(); + } else { + mUserHasAuthenticatedSinceBoot.put(userId, false); + } + } + } + + private void requireCredentialEntry(int userId) { + clearUserHasAuthenticated(userId); + refreshAgentList(userId); + } + // Listeners private void addListener(ITrustListener listener) { @@ -705,6 +728,18 @@ public class TrustManagerService extends SystemService { } } + @Override + public boolean hasUserAuthenticatedSinceBoot(int userId) throws RemoteException { + mContext.enforceCallingOrSelfPermission( + Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE, null); + long token = Binder.clearCallingIdentity(); + try { + return getUserHasAuthenticated(userId); + } finally { + Binder.restoreCallingIdentity(token); + } + } + private void enforceReportPermission() { mContext.enforceCallingOrSelfPermission( Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE, "reporting trust events"); diff --git a/telecomm/java/android/telecom/DefaultDialerManager.java b/telecomm/java/android/telecom/DefaultDialerManager.java index b5d566a..fd0c06d 100644 --- a/telecomm/java/android/telecom/DefaultDialerManager.java +++ b/telecomm/java/android/telecom/DefaultDialerManager.java @@ -14,6 +14,7 @@ package android.telecom; +import android.app.ActivityManager; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; @@ -35,15 +36,27 @@ public class DefaultDialerManager { private static final String TAG = "DefaultDialerManager"; /** - * Sets the specified package name as the default dialer application. The caller of this method - * needs to have permission to write to secure settings. + * Sets the specified package name as the default dialer application for the current user. + * The caller of this method needs to have permission to write to secure settings and + * manage users on the device. * * @hide * */ public static void setDefaultDialerApplication(Context context, String packageName) { + setDefaultDialerApplication(context, packageName, ActivityManager.getCurrentUser()); + } + + /** + * Sets the specified package name as the default dialer application for the specified user. + * The caller of this method needs to have permission to write to secure settings and + * manage users on the device. + * + * @hide + * */ + public static void setDefaultDialerApplication(Context context, String packageName, int user) { // Get old package name - String oldPackageName = Settings.Secure.getString(context.getContentResolver(), - Settings.Secure.DIALER_DEFAULT_APPLICATION); + String oldPackageName = Settings.Secure.getStringForUser(context.getContentResolver(), + Settings.Secure.DIALER_DEFAULT_APPLICATION, user); if (packageName != null && oldPackageName != null && packageName.equals(oldPackageName)) { // No change @@ -55,26 +68,44 @@ public class DefaultDialerManager { if (packageNames.contains(packageName)) { // Update the secure setting. - Settings.Secure.putString(context.getContentResolver(), - Settings.Secure.DIALER_DEFAULT_APPLICATION, packageName); + Settings.Secure.putStringForUser(context.getContentResolver(), + Settings.Secure.DIALER_DEFAULT_APPLICATION, packageName, user); } } /** - * Returns the installed dialer application that will be used to receive incoming calls, and is - * allowed to make emergency calls. + * Returns the installed dialer application for the current user that will be used to receive + * incoming calls, and is allowed to make emergency calls. * * The application will be returned in order of preference: * 1) User selected phone application (if still installed) * 2) Pre-installed system dialer (if not disabled) * 3) Null * + * The caller of this method needs to have permission to manage users on the device. + * * @hide * */ public static String getDefaultDialerApplication(Context context) { - String defaultPackageName = Settings.Secure.getString(context.getContentResolver(), - Settings.Secure.DIALER_DEFAULT_APPLICATION); + return getDefaultDialerApplication(context, ActivityManager.getCurrentUser()); + } + /** + * Returns the installed dialer application for the specified user that will be used to receive + * incoming calls, and is allowed to make emergency calls. + * + * The application will be returned in order of preference: + * 1) User selected phone application (if still installed) + * 2) Pre-installed system dialer (if not disabled) + * 3) Null + * + * The caller of this method needs to have permission to manage users on the device. + * + * @hide + * */ + public static String getDefaultDialerApplication(Context context, int user) { + String defaultPackageName = Settings.Secure.getStringForUser(context.getContentResolver(), + Settings.Secure.DIALER_DEFAULT_APPLICATION, user); final List<String> packageNames = getInstalledDialerApplications(context); diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java index 85a489b..f563839 100644 --- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java +++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java @@ -317,6 +317,7 @@ public class TelephonyIntents { public static final String EXTRA_PLMN = "plmn"; public static final String EXTRA_SHOW_SPN = "showSpn"; public static final String EXTRA_SPN = "spn"; + public static final String EXTRA_DATA_SPN = "spnData"; /** * <p>Broadcast Action: It indicates one column of a subinfo record has been changed diff --git a/tests/MemoryUsage/AndroidManifest.xml b/tests/MemoryUsage/AndroidManifest.xml index 3932e5b..cd559c5 100644 --- a/tests/MemoryUsage/AndroidManifest.xml +++ b/tests/MemoryUsage/AndroidManifest.xml @@ -6,7 +6,9 @@ android:name="com.android.tests.memoryusage.MemoryUsageInstrumentation" android:targetPackage="com.android.tests.memoryusage" /> + <uses-permission android:name="android.permission.REAL_GET_TASKS" /> + <application android:label="Memory Usage Test"> <uses-library android:name="android.test.runner" /> </application> -</manifest>
\ No newline at end of file +</manifest> |