summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/current.txt12
-rw-r--r--api/system-current.txt14
-rw-r--r--core/java/android/app/Activity.java5
-rw-r--r--core/java/android/app/ActivityManager.java22
-rw-r--r--core/java/android/app/AppOpsManager.java3
-rw-r--r--core/java/android/app/Fragment.java6
-rw-r--r--core/java/android/app/FragmentHostCallback.java17
-rw-r--r--core/java/android/app/FragmentManager.java8
-rw-r--r--core/java/android/app/admin/DeviceAdminReceiver.java8
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java2
-rw-r--r--core/java/android/app/trust/ITrustManager.aidl1
-rw-r--r--core/java/android/app/trust/TrustManager.java17
-rw-r--r--core/java/android/content/pm/ActivityInfo.java24
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl4
-rw-r--r--core/java/android/content/res/AssetManager.java1
-rw-r--r--core/java/android/content/res/Resources.java13
-rw-r--r--core/java/android/service/notification/ZenModeConfig.java13
-rw-r--r--core/java/android/text/Html.java2
-rw-r--r--core/java/android/view/DisplayListCanvas.java6
-rw-r--r--core/java/android/view/ThreadedRenderer.java9
-rw-r--r--core/java/com/android/internal/os/BinderInternal.java4
-rwxr-xr-xcore/jni/android/graphics/Bitmap.cpp592
-rw-r--r--core/jni/android/graphics/Bitmap.h114
-rw-r--r--core/jni/android/graphics/BitmapFactory.cpp52
-rw-r--r--core/jni/android/graphics/BitmapRegionDecoder.cpp24
-rw-r--r--core/jni/android/graphics/Graphics.cpp120
-rw-r--r--core/jni/android/graphics/GraphicsJNI.h73
-rw-r--r--core/jni/android_media_AudioRecord.cpp1
-rw-r--r--core/jni/android_util_AssetManager.cpp9
-rw-r--r--core/jni/android_view_DisplayListCanvas.cpp20
-rw-r--r--core/jni/android_view_SurfaceControl.cpp22
-rw-r--r--core/jni/android_view_ThreadedRenderer.cpp10
-rw-r--r--core/res/AndroidManifest.xml6
-rwxr-xr-xcore/res/res/values-mcc311-mnc480/config.xml2
-rw-r--r--core/res/res/values/strings.xml2
-rwxr-xr-xcore/res/res/values/symbols.xml1
-rw-r--r--graphics/java/android/graphics/Bitmap.java88
-rw-r--r--include/androidfw/ResourceTypes.h7
-rw-r--r--libs/androidfw/ResourceTypes.cpp10
-rw-r--r--libs/hwui/Android.common.mk1
-rw-r--r--libs/hwui/Caches.cpp117
-rw-r--r--libs/hwui/Caches.h46
-rw-r--r--libs/hwui/DeferredDisplayList.cpp3
-rw-r--r--libs/hwui/DrawProfiler.cpp42
-rw-r--r--libs/hwui/DrawProfiler.h38
-rw-r--r--libs/hwui/GlopBuilder.cpp4
-rw-r--r--libs/hwui/OpenGLRenderer.cpp44
-rw-r--r--libs/hwui/PathCache.cpp2
-rw-r--r--libs/hwui/Properties.cpp149
-rw-r--r--libs/hwui/Properties.h67
-rw-r--r--libs/hwui/ShadowTessellator.cpp23
-rw-r--r--libs/hwui/TessellationCache.cpp2
-rw-r--r--libs/hwui/TextDropShadowCache.cpp2
-rw-r--r--libs/hwui/TextureCache.cpp2
-rw-r--r--libs/hwui/renderstate/RenderState.cpp2
-rw-r--r--libs/hwui/renderthread/RenderProxy.cpp39
-rw-r--r--libs/hwui/renderthread/RenderProxy.h3
-rw-r--r--libs/hwui/renderthread/RenderThread.cpp1
-rw-r--r--libs/hwui/tests/TestContext.cpp5
-rw-r--r--libs/hwui/tests/how_to_run.txt17
-rw-r--r--libs/hwui/tests/main.cpp20
-rw-r--r--libs/hwui/unit_tests/Android.mk1
-rw-r--r--libs/hwui/unit_tests/DamageAccumulatorTests.cpp81
-rw-r--r--media/java/android/media/MediaCodecInfo.java80
-rw-r--r--media/java/android/media/tv/TvContentRating.java11
-rw-r--r--media/java/android/media/tv/TvInputManager.java2
-rw-r--r--media/java/android/media/tv/TvInputService.java14
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java1
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java25
-rw-r--r--media/tests/contents/media_api/video/MPEG2_1500_AAC_128.mp4bin0 -> 2100454 bytes
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/CopyService.java27
-rw-r--r--packages/DocumentsUI/tests/src/com/android/documentsui/CopyTest.java290
-rw-r--r--packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java100
-rw-r--r--packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java11
-rw-r--r--packages/SettingsLib/res/values/strings.xml8
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java25
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java216
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java12
-rw-r--r--packages/SystemUI/res/values/strings.xml6
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileDataControllerImpl.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java76
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java2
-rw-r--r--rs/java/android/renderscript/RenderScript.java18
-rw-r--r--rs/jni/android_renderscript_RenderScript.cpp208
-rw-r--r--services/core/java/com/android/server/InputMethodManagerService.java27
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java26
-rw-r--r--services/core/java/com/android/server/notification/CalendarTracker.java38
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java46
-rw-r--r--services/core/java/com/android/server/pm/Settings.java8
-rw-r--r--services/core/java/com/android/server/trust/TrustManagerService.java59
-rw-r--r--telecomm/java/android/telecom/DefaultDialerManager.java51
-rw-r--r--telephony/java/com/android/internal/telephony/TelephonyIntents.java1
-rw-r--r--tests/MemoryUsage/AndroidManifest.xml4
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
new file mode 100644
index 0000000..33f66a0
--- /dev/null
+++ b/media/tests/contents/media_api/video/MPEG2_1500_AAC_128.mp4
Binary files differ
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>