From a381c4ced38de01c88f7e660d5dab85607675ce4 Mon Sep 17 00:00:00 2001 From: Siyamed Sinir Date: Tue, 28 Nov 2017 13:29:18 -0800 Subject: [PATCH 001/671] DO NOT MERGE Fix mTrustManager NPE When isDeviceLocked function is called in KeyguardManager, mTrustManager can be null. To prevent NPE during this call, moved the mTrustManager access to a synchronized getter. Test: run cts -c android.print.cts.PageRangeAdjustmentTest -m testWantedPagesAlreadyWrittenForPreview Test: run cts -c android.accessibilityservice.cts.AccessibilityEndToEndTest -m testTypeViewTextChangedAccessibilityEvent Test: run cts -c com.android.cts.appsecurity.DocumentsTest -m testCreateExisting Test: run cts -c com.android.cts.devicepolicy.ManagedProfileTest -m testCrossProfileCopyPaste Test: run cts -c android.text.method.cts.PasswordTransformationMethodTest Bug: 69471788 Change-Id: I4b4a0bb3b127424fecdad85ba559ce861af165e4 --- core/java/android/app/KeyguardManager.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java index 2c1231773bb..4bebdfa7e4d 100644 --- a/core/java/android/app/KeyguardManager.java +++ b/core/java/android/app/KeyguardManager.java @@ -249,7 +249,7 @@ public class KeyguardManager { */ public boolean isDeviceLocked(int userId) { try { - return mTrustManager.isDeviceLocked(userId); + return getTrustManager().isDeviceLocked(userId); } catch (RemoteException e) { return false; } @@ -274,12 +274,20 @@ public class KeyguardManager { */ public boolean isDeviceSecure(int userId) { try { - return mTrustManager.isDeviceSecure(userId); + return getTrustManager().isDeviceSecure(userId); } catch (RemoteException e) { return false; } } + private synchronized ITrustManager getTrustManager() { + if (mTrustManager == null) { + mTrustManager = ITrustManager.Stub.asInterface( + ServiceManager.getService(Context.TRUST_SERVICE)); + } + return mTrustManager; + } + /** * @deprecated Use {@link android.view.WindowManager.LayoutParams#FLAG_DISMISS_KEYGUARD} * and/or {@link android.view.WindowManager.LayoutParams#FLAG_SHOW_WHEN_LOCKED} -- GitLab From f55db8c3641886a8307441488a62fad22439af0a Mon Sep 17 00:00:00 2001 From: Nick Butcher Date: Tue, 9 Jan 2018 15:24:21 +0000 Subject: [PATCH 002/671] Prevent AAPT from versioning tags. Bug: 69359529 Test: Manually tested with ag/3178054 Change-Id: I10ae4d96c2a31a0a7c363d7a9292ecdfd3bb526a --- tools/aapt/ResourceTable.cpp | 4 +++- tools/aapt2/cmd/Link.cpp | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp index 669afe18af8..734a5ab8aab 100644 --- a/tools/aapt/ResourceTable.cpp +++ b/tools/aapt/ResourceTable.cpp @@ -4848,6 +4848,7 @@ status_t ResourceTable::modifyForCompat(const Bundle* bundle, const String16 pathInterpolator16("pathInterpolator"); const String16 objectAnimator16("objectAnimator"); const String16 gradient16("gradient"); + const String16 animatedSelector16("animated-selector"); const int minSdk = getMinSdkVersion(bundle); if (minSdk >= SDK_LOLLIPOP_MR1) { @@ -4876,7 +4877,8 @@ status_t ResourceTable::modifyForCompat(const Bundle* bundle, node->getElementName() == animatedVector16 || node->getElementName() == objectAnimator16 || node->getElementName() == pathInterpolator16 || - node->getElementName() == gradient16)) { + node->getElementName() == gradient16 || + node->getElementName() == animatedSelector16)) { // We were told not to version vector tags, so skip the children here. continue; } diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp index 5fc35b81121..1f56300026e 100644 --- a/tools/aapt2/cmd/Link.cpp +++ b/tools/aapt2/cmd/Link.cpp @@ -442,7 +442,7 @@ static bool IsTransitionElement(const std::string& name) { static bool IsVectorElement(const std::string& name) { return name == "vector" || name == "animated-vector" || name == "pathInterpolator" || - name == "objectAnimator" || name == "gradient"; + name == "objectAnimator" || name == "gradient" || name == "animated-selector"; } template -- GitLab From 03955ea77748a76f1769c1a01350771c83143560 Mon Sep 17 00:00:00 2001 From: "yunseon.park" Date: Tue, 26 Dec 2017 09:47:14 +0900 Subject: [PATCH 003/671] Set the WebViewLoader's targetSdk same with framework's. 1. WebViewLoader starts with targetSdk 0 at startIsolatedProcess api. 2. Killed by killAllBackgroundProcessesExcept when density changed. Bug: 70655801 Change-Id: I8fcfdb0f958063b1fa81a20d9ba6b1c862656852 Signed-off-by: Yunseon Park --- .../core/java/com/android/server/am/ActivityManagerService.java | 1 + 1 file changed, 1 insertion(+) diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index dce0c385223..44883c1db8b 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -3634,6 +3634,7 @@ public class ActivityManagerService extends IActivityManager.Stub info.className = entryPoint; info.packageName = "android"; info.seInfoUser = SELinuxUtil.COMPLETE_STR; + info.targetSdkVersion = Build.VERSION.SDK_INT; ProcessRecord proc = startProcessLocked(processName, info /* info */, false /* knownToBeDead */, 0 /* intentFlags */, "" /* hostingType */, null /* hostingName */, true /* allowWhileBooting */, true /* isolated */, -- GitLab From 2f184df0f5301edd66f1290d5d00f876af45ad72 Mon Sep 17 00:00:00 2001 From: Felipe Leme Date: Thu, 7 Dec 2017 15:54:27 -0800 Subject: [PATCH 004/671] Improved logging on Autofill Save. Test: looked at adb logcat while running CTS tests Test: atest CtsAutoFillServiceTestCases Bug: 70348203 Change-Id: I115d639dacaeb85792ddaab2465dd0187fd1d362 --- .../java/com/android/server/autofill/Session.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index f5d1336a0f6..c71a60cc6f1 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -926,7 +926,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final int flags = lastResponse.getFlags(); if ((flags & FillResponse.FLAG_TRACK_CONTEXT_COMMITED) == 0) { - if (sDebug) Slog.d(TAG, "logContextCommittedLocked(): ignored by flags " + flags); + if (sVerbose) Slog.v(TAG, "logContextCommittedLocked(): ignored by flags " + flags); return; } @@ -1404,7 +1404,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - if (sDebug) Slog.d(TAG, "Good news, everyone! All checks passed, show save UI!"); + if (sDebug) { + Slog.d(TAG, "Good news, everyone! All checks passed, show save UI for " + + id + "!"); + } // Use handler so logContextCommitted() is logged first mHandlerCaller.getHandler().post(() -> mService.logSaveShown(id, mClientState)); @@ -1428,7 +1431,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } // Nothing changed... if (sDebug) { - Slog.d(TAG, "showSaveLocked(): with no changes, comes no responsibilities." + Slog.d(TAG, "showSaveLocked(" + id +"): with no changes, comes no responsibilities." + "allRequiredAreNotNull=" + allRequiredAreNotEmpty + ", atLeastOneChanged=" + atLeastOneChanged); } @@ -1943,7 +1946,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState try { if (sVerbose) { Slog.v(TAG, "updateTrackedIdsLocked(): " + trackedViews + " => " + fillableIds - + " (triggering on " + saveTriggerId + ")"); + + " triggerId: " + saveTriggerId + " saveOnFinish:" + saveOnFinish); } mClient.setTrackedViews(id, toArray(trackedViews), saveOnAllViewsInvisible, saveOnFinish, toArray(fillableIds), saveTriggerId); -- GitLab From 1a0839393e33d41fdf2330685ece399bf70640f9 Mon Sep 17 00:00:00 2001 From: Rhiannon Malia Date: Wed, 24 Jan 2018 15:02:30 -0800 Subject: [PATCH 005/671] Adding suppressShowOverApps to TvExtender This allows senders to override the default behavior on launcher for max and high level notifications so that heads up messages for those notifications do not pop up over non-launcher apps. Test: ag/3508615 Change-Id: I90b85a5e959c1980a91881d23749e0f2b7b98b3b Fixes: 72409064 --- api/system-current.txt | 2 ++ core/java/android/app/Notification.java | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/api/system-current.txt b/api/system-current.txt index 2ed8f90e3c7..fcde5ef288e 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -335,11 +335,13 @@ package android.app { method public java.lang.String getChannelId(); method public android.app.PendingIntent getContentIntent(); method public android.app.PendingIntent getDeleteIntent(); + method public boolean getSuppressShowOverApps(); method public boolean isAvailableOnTv(); method public android.app.Notification.TvExtender setChannel(java.lang.String); method public android.app.Notification.TvExtender setChannelId(java.lang.String); method public android.app.Notification.TvExtender setContentIntent(android.app.PendingIntent); method public android.app.Notification.TvExtender setDeleteIntent(android.app.PendingIntent); + method public android.app.Notification.TvExtender setSuppressShowOverApps(boolean); } public final class NotificationChannel implements android.os.Parcelable { diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index d6fddfca986..b01fbb28028 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -8845,6 +8845,7 @@ public class Notification implements Parcelable private static final String EXTRA_CONTENT_INTENT = "content_intent"; private static final String EXTRA_DELETE_INTENT = "delete_intent"; private static final String EXTRA_CHANNEL_ID = "channel_id"; + private static final String EXTRA_SUPPRESS_SHOW_OVER_APPS = "suppressShowOverApps"; // Flags bitwise-ored to mFlags private static final int FLAG_AVAILABLE_ON_TV = 0x1; @@ -8853,6 +8854,7 @@ public class Notification implements Parcelable private String mChannelId; private PendingIntent mContentIntent; private PendingIntent mDeleteIntent; + private boolean mSuppressShowOverApps; /** * Create a {@link TvExtender} with default options. @@ -8872,6 +8874,7 @@ public class Notification implements Parcelable if (bundle != null) { mFlags = bundle.getInt(EXTRA_FLAGS); mChannelId = bundle.getString(EXTRA_CHANNEL_ID); + mSuppressShowOverApps = bundle.getBoolean(EXTRA_SUPPRESS_SHOW_OVER_APPS); mContentIntent = bundle.getParcelable(EXTRA_CONTENT_INTENT); mDeleteIntent = bundle.getParcelable(EXTRA_DELETE_INTENT); } @@ -8888,6 +8891,7 @@ public class Notification implements Parcelable bundle.putInt(EXTRA_FLAGS, mFlags); bundle.putString(EXTRA_CHANNEL_ID, mChannelId); + bundle.putBoolean(EXTRA_SUPPRESS_SHOW_OVER_APPS, mSuppressShowOverApps); if (mContentIntent != null) { bundle.putParcelable(EXTRA_CONTENT_INTENT, mContentIntent); } @@ -8980,6 +8984,23 @@ public class Notification implements Parcelable public PendingIntent getDeleteIntent() { return mDeleteIntent; } + + /** + * Specifies whether this notification should suppress showing a message over top of apps + * outside of the launcher. + */ + public TvExtender setSuppressShowOverApps(boolean suppress) { + mSuppressShowOverApps = suppress; + return this; + } + + /** + * Returns true if this notification should not show messages over top of apps + * outside of the launcher. + */ + public boolean getSuppressShowOverApps() { + return mSuppressShowOverApps; + } } /** -- GitLab From a5bca091d8d6c5e4868835d9a641164084561b20 Mon Sep 17 00:00:00 2001 From: Sudheer Shanka Date: Thu, 25 Jan 2018 10:41:43 -0800 Subject: [PATCH 006/671] check if per-uid cputimes proc file has correct no. of freqs. Bug: 66953194 Test: atest core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java Change-Id: I0f5b00dcdb70ff0dcbed3455cf06f2ada21459ff --- .../os/KernelSingleUidTimeReader.java | 38 ++++++++++++++++--- .../os/KernelUidCpuFreqTimeReader.java | 2 +- .../os/KernelSingleUidTimeReaderTest.java | 24 ++++++++++++ 3 files changed, 58 insertions(+), 6 deletions(-) diff --git a/core/java/com/android/internal/os/KernelSingleUidTimeReader.java b/core/java/com/android/internal/os/KernelSingleUidTimeReader.java index ebeb24c4147..42839171dc5 100644 --- a/core/java/com/android/internal/os/KernelSingleUidTimeReader.java +++ b/core/java/com/android/internal/os/KernelSingleUidTimeReader.java @@ -16,6 +16,7 @@ package com.android.internal.os; import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; +import static com.android.internal.os.KernelUidCpuFreqTimeReader.UID_TIMES_PROC_FILE; import android.annotation.NonNull; import android.util.Slog; @@ -54,6 +55,12 @@ public class KernelSingleUidTimeReader { private boolean mSingleUidCpuTimesAvailable = true; @GuardedBy("this") private boolean mHasStaleData; + // We use the freq count obtained from /proc/uid_time_in_state to decide how many longs + // to read from each /proc/uid//time_in_state. On the first read, verify if this is + // correct and if not, set {@link #mSingleUidCpuTimesAvailable} to false. This flag will + // indicate whether we checked for validity or not. + @GuardedBy("this") + private boolean mCpuFreqsCountVerified; private final Injector mInjector; @@ -82,15 +89,15 @@ public class KernelSingleUidTimeReader { final String procFile = new StringBuilder(PROC_FILE_DIR) .append(uid) .append(PROC_FILE_NAME).toString(); - final long[] cpuTimesMs = new long[mCpuFreqsCount]; + final long[] cpuTimesMs; try { final byte[] data = mInjector.readData(procFile); + if (!mCpuFreqsCountVerified) { + verifyCpuFreqsCount(data.length, procFile); + } final ByteBuffer buffer = ByteBuffer.wrap(data); buffer.order(ByteOrder.nativeOrder()); - for (int i = 0; i < mCpuFreqsCount; ++i) { - // Times read will be in units of 10ms - cpuTimesMs[i] = buffer.getLong() * 10; - } + cpuTimesMs = readCpuTimesFromByteBuffer(buffer); } catch (Exception e) { if (++mReadErrorCounter >= TOTAL_READ_ERROR_COUNT) { mSingleUidCpuTimesAvailable = false; @@ -103,6 +110,27 @@ public class KernelSingleUidTimeReader { } } + private void verifyCpuFreqsCount(int numBytes, String procFile) { + final int actualCount = (numBytes / Long.BYTES); + if (mCpuFreqsCount != actualCount) { + mSingleUidCpuTimesAvailable = false; + throw new IllegalStateException("Freq count didn't match," + + "count from " + UID_TIMES_PROC_FILE + "=" + mCpuFreqsCount + ", but" + + "count from " + procFile + "=" + actualCount); + } + mCpuFreqsCountVerified = true; + } + + private long[] readCpuTimesFromByteBuffer(ByteBuffer buffer) { + final long[] cpuTimesMs; + cpuTimesMs = new long[mCpuFreqsCount]; + for (int i = 0; i < mCpuFreqsCount; ++i) { + // Times read will be in units of 10ms + cpuTimesMs[i] = buffer.getLong() * 10; + } + return cpuTimesMs; + } + /** * Compute and return cpu times delta of an uid using previously read cpu times and * {@param latestCpuTimesMs}. diff --git a/core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java b/core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java index b8982cce91b..d97538c3049 100644 --- a/core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java +++ b/core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java @@ -49,7 +49,7 @@ import java.io.IOException; public class KernelUidCpuFreqTimeReader { private static final boolean DEBUG = false; private static final String TAG = "KernelUidCpuFreqTimeReader"; - private static final String UID_TIMES_PROC_FILE = "/proc/uid_time_in_state"; + static final String UID_TIMES_PROC_FILE = "/proc/uid_time_in_state"; public interface Callback { void onUidCpuFreqTime(int uid, long[] cpuFreqTimeMs); diff --git a/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java index 5d72942b53f..29227f9f6a1 100644 --- a/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java +++ b/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java @@ -114,6 +114,30 @@ public class KernelSingleUidTimeReaderTest { assertFalse(mReader.singleUidCpuTimesAvailable()); } + @Test + public void readDelta_incorrectCount() { + assertTrue(mReader.singleUidCpuTimesAvailable()); + + long[] cpuTimes = new long[TEST_FREQ_COUNT - 1]; + for (int i = 0; i < cpuTimes.length; ++i) { + cpuTimes[i] = 111 + i; + } + mInjector.setData(cpuTimes); + assertCpuTimesEqual(null, mReader.readDeltaMs(TEST_UID)); + assertFalse(mReader.singleUidCpuTimesAvailable()); + + // Reset + mReader.setSingleUidCpuTimesAvailable(true); + + cpuTimes = new long[TEST_FREQ_COUNT + 1]; + for (int i = 0; i < cpuTimes.length; ++i) { + cpuTimes[i] = 222 + i; + } + mInjector.setData(cpuTimes); + assertCpuTimesEqual(null, mReader.readDeltaMs(TEST_UID)); + assertFalse(mReader.singleUidCpuTimesAvailable()); + } + @Test public void testComputeDelta() { // proc file not available -- GitLab From a6dfa74dc524c4566d8bab08fb870fcfe235ddde Mon Sep 17 00:00:00 2001 From: Vladislav Kaznacheev Date: Thu, 25 Jan 2018 13:59:35 -0800 Subject: [PATCH 007/671] Fix context submenu position Clear the list of presenters in MenuBuilder when the MenuBuilder instance is reset via clearAll. This prevents MenuPresenter instances from accumulating and ensures that a stale instance MenuPresenter is not activated. Bug: 72507876 Test: android.view.menu.ContextMenuTest Change-Id: I4911ca31307bc93901987f08298fa6b2926ba6ab --- core/java/com/android/internal/view/menu/MenuBuilder.java | 1 + .../coretests/src/android/view/menu/ContextMenuTest.java | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/core/java/com/android/internal/view/menu/MenuBuilder.java b/core/java/com/android/internal/view/menu/MenuBuilder.java index b53459e072d..67dc81af589 100644 --- a/core/java/com/android/internal/view/menu/MenuBuilder.java +++ b/core/java/com/android/internal/view/menu/MenuBuilder.java @@ -551,6 +551,7 @@ public class MenuBuilder implements Menu { mPreventDispatchingItemsChanged = true; clear(); clearHeader(); + mPresenters.clear(); mPreventDispatchingItemsChanged = false; mItemsChangedWhileDispatchPrevented = false; onItemsChanged(true); diff --git a/core/tests/coretests/src/android/view/menu/ContextMenuTest.java b/core/tests/coretests/src/android/view/menu/ContextMenuTest.java index 59d4e55d8d4..657a7fc7b79 100644 --- a/core/tests/coretests/src/android/view/menu/ContextMenuTest.java +++ b/core/tests/coretests/src/android/view/menu/ContextMenuTest.java @@ -41,6 +41,13 @@ public class ContextMenuTest extends ActivityInstrumentationTestCase Date: Thu, 25 Jan 2018 16:03:39 -0800 Subject: [PATCH 008/671] [QS] Change secondary label for night light If the night light is scheduled and the minute reading is 0, we'll continue showing just the hour. But for non-zero minute schedules, we'll show "hour:minutes" instead. Test: visually Bug:69973398 Change-Id: Ida78aa0b872b7f8b6f8cd84fa559ff44c71175cd --- .../systemui/qs/tiles/NightDisplayTile.java | 37 +++++++++++++------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java index ea6e174d786..3597929229e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java @@ -21,6 +21,7 @@ import android.app.ActivityManager; import android.content.Intent; import android.provider.Settings; import android.service.quicksettings.Tile; +import android.support.annotation.StringRes; import android.widget.Switch; import com.android.internal.app.ColorDisplayController; @@ -30,6 +31,7 @@ import com.android.systemui.qs.QSHost; import com.android.systemui.plugins.qs.QSTile.BooleanState; import com.android.systemui.qs.tileimpl.QSTileImpl; +import java.time.LocalTime; import java.time.format.DateTimeFormatter; public class NightDisplayTile extends QSTileImpl @@ -39,7 +41,9 @@ public class NightDisplayTile extends QSTileImpl * Pattern for {@link java.time.format.DateTimeFormatter} used to approximate the time to the * nearest hour and add on the AM/PM indicator. */ - private static final String APPROXIMATE_HOUR_DATE_TIME_PATTERN = "h a"; + private static final String HOUR_MINUTE_DATE_TIME_PATTERN = "h a"; + private static final String APPROXIMATE_HOUR_DATE_TIME_PATTERN = "h:m a"; + private ColorDisplayController mController; private boolean mIsListening; @@ -110,17 +114,26 @@ public class NightDisplayTile extends QSTileImpl case ColorDisplayController.AUTO_MODE_CUSTOM: // User-specified time, approximated to the nearest hour. - return isNightLightActivated - ? mContext.getString( - R.string.quick_settings_night_secondary_label_until, - mController.getCustomEndTime().format( - DateTimeFormatter.ofPattern( - APPROXIMATE_HOUR_DATE_TIME_PATTERN))) - : mContext.getString( - R.string.quick_settings_night_secondary_label_on_at, - mController.getCustomStartTime().format( - DateTimeFormatter.ofPattern( - APPROXIMATE_HOUR_DATE_TIME_PATTERN))); + final @StringRes int toggleTimeStringRes; + final LocalTime toggleTime; + final DateTimeFormatter toggleTimeFormat; + + if (isNightLightActivated) { + toggleTime = mController.getCustomEndTime(); + toggleTimeStringRes = R.string.quick_settings_night_secondary_label_until; + } else { + toggleTime = mController.getCustomStartTime(); + toggleTimeStringRes = R.string.quick_settings_night_secondary_label_on_at; + } + + // Choose between just showing the hour or also showing the minutes (based on the + // user-selected toggle time). This helps reduce how much space the label takes. + toggleTimeFormat = DateTimeFormatter.ofPattern( + toggleTime.getMinute() == 0 + ? HOUR_MINUTE_DATE_TIME_PATTERN + : APPROXIMATE_HOUR_DATE_TIME_PATTERN); + + return mContext.getString(toggleTimeStringRes, toggleTime.format(toggleTimeFormat)); default: // No secondary label when auto mode is disabled. -- GitLab From f7846036a6de47bcd0a824ebd4772684c228f62a Mon Sep 17 00:00:00 2001 From: jackqdyulei Date: Mon, 29 Jan 2018 14:49:37 -0800 Subject: [PATCH 009/671] Change AccessPointPreference to normal Preference Make it extend from normal Preference Change-Id: Iee1ea7caedb8a5eb530545d085c96b877e3f7a00 Fixes: 72528535 Fixes: 72442172 Test: Build --- .../settingslib/wifi/AccessPointPreference.java | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java index dd55188e390..315eee48d2f 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java @@ -41,7 +41,7 @@ import com.android.settingslib.TwoTargetPreference; import com.android.settingslib.Utils; import com.android.settingslib.wifi.AccessPoint.Speed; -public class AccessPointPreference extends TwoTargetPreference { +public class AccessPointPreference extends Preference { private static final int[] STATE_SECURED = { R.attr.state_encrypted @@ -128,6 +128,7 @@ public class AccessPointPreference extends TwoTargetPreference { int iconResId, boolean forSavedNetworks, StateListDrawable frictionSld, int level, IconInjector iconInjector) { super(context); + setWidgetLayoutResource(R.layout.access_point_friction_widget); mBadgeCache = cache; mAccessPoint = accessPoint; mForSavedNetworks = forSavedNetworks; @@ -166,20 +167,6 @@ public class AccessPointPreference extends TwoTargetPreference { ImageView frictionImageView = (ImageView) view.findViewById(R.id.friction_icon); bindFrictionImage(frictionImageView); - setDividerVisibility(view, View.GONE); - } - - protected void setDividerVisibility(final PreferenceViewHolder view, - @View.Visibility int visibility) { - final View divider = view.findViewById(R.id.two_target_divider); - if (divider != null) { - divider.setVisibility(visibility); - } - } - - @Override - protected int getSecondTargetResId() { - return R.layout.access_point_friction_widget; } protected void updateIcon(int level, Context context) { -- GitLab From 64ece0e5823cf21842d7679a55d5f5c124a12a03 Mon Sep 17 00:00:00 2001 From: Gurpreet Ghai Date: Wed, 10 Aug 2016 15:14:40 +0530 Subject: [PATCH 010/671] Fix ANR due to long wait for synchronization lock Use Case: Repeated BT ON/OFF Failure: ANR occurs due to UI wait for long time waiting to acquire thread lock. Steps: Repeated BT ON/OFF Root Cause: The synchronized function that updates state also read paired devices as an additional operation. When the number of devices is cached list is large, the block time for other threads waiting for same lock tends to increase causing ANR. Fix: Limited the synchronized block to the part where actual update of local state takes place. Test: SNS Testing Bug: 35412140 Change-Id: I69ff9f8a032b3772bf3d048d8db70181319ad31d --- .../settingslib/bluetooth/LocalBluetoothAdapter.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) mode change 100755 => 100644 packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java old mode 100755 new mode 100644 index cda4e454fe7..5f7ba586fba --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java @@ -194,8 +194,13 @@ public class LocalBluetoothAdapter { return mState; } - synchronized void setBluetoothStateInt(int state) { - mState = state; + void setBluetoothStateInt(int state) { + synchronized(this) { + if (mState == state) { + return; + } + mState = state; + } if (state == BluetoothAdapter.STATE_ON) { // if mProfileManager hasn't been constructed yet, it will -- GitLab From 35dbf35b26430999f1e07dfb129822c2506f3b4b Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Fri, 26 Jan 2018 20:41:17 -0800 Subject: [PATCH 011/671] Frameworks: Mark tests Add @Ignore and @Test to make Errorprone happy. Bug: 72076216 Test: m javac-check RUN_ERROR_PRONE=true Test: atest ConnectivityServiceTest Test: atest WifiManagerTest Change-Id: Id2423c545eccaa768203faf86e14d0a558d927cd --- tests/net/java/com/android/server/ConnectivityServiceTest.java | 1 + wifi/tests/src/android/net/wifi/WifiManagerTest.java | 1 + 2 files changed, 2 insertions(+) diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index 6e643a3dae1..e7abede4cda 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -318,6 +318,7 @@ public class ConnectivityServiceTest { // This test has an inherent race condition in it, and cannot be enabled for continuous testing // or presubmit tests. It is kept for manual runs and documentation purposes. + @Ignore public void verifyThatNotWaitingForIdleCausesRaceConditions() { // Bring up a network that we can use to send messages to ConnectivityService. ConditionVariable cv = waitForConnectivityBroadcasts(1); diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java index b235ccc7a89..8ac659f83b5 100644 --- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java +++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java @@ -599,6 +599,7 @@ public class WifiManagerTest { /** * Verify the watchLocalOnlyHotspot call goes to WifiServiceImpl. */ + @Test public void testWatchLocalOnlyHotspot() throws Exception { TestLocalOnlyHotspotObserver observer = new TestLocalOnlyHotspotObserver(); -- GitLab From b144227742ada7fd693684a5bd2d53abd8e7c499 Mon Sep 17 00:00:00 2001 From: Neil Fuller Date: Mon, 18 Dec 2017 15:59:50 +0000 Subject: [PATCH 012/671] Add support for time zone notifications Add support for time zone notifications to RulesManagerService. This change adds new intents that are broadcast after a time zone rules update is staged / unstaged. This will allow another component on the device to handle notifications telling the user to restart the device. Bug: 69443060 Test: atest FrameworksServicesTests Test: Manual install Change-Id: I4fc4b5715380d84d87cdb43c05aee2f8f9c5d277 --- .../android/app/timezone/RulesManager.java | 17 +++++++ core/res/AndroidManifest.xml | 4 ++ .../server/timezone/PackageTracker.java | 6 +-- ...r.java => PackageTrackerIntentHelper.java} | 2 +- ...va => PackageTrackerIntentHelperImpl.java} | 8 ++-- .../timezone/RulesManagerIntentHelper.java | 35 +++++++++++++++ .../server/timezone/RulesManagerService.java | 36 ++++++++++++++- .../RulesManagerServiceHelperImpl.java | 30 ++++++++++--- .../server/timezone/PackageTrackerTest.java | 3 +- .../timezone/RulesManagerServiceTest.java | 45 +++++++++++++++++++ 10 files changed, 167 insertions(+), 19 deletions(-) rename services/core/java/com/android/server/timezone/{IntentHelper.java => PackageTrackerIntentHelper.java} (97%) rename services/core/java/com/android/server/timezone/{IntentHelperImpl.java => PackageTrackerIntentHelperImpl.java} (93%) create mode 100644 services/core/java/com/android/server/timezone/RulesManagerIntentHelper.java diff --git a/core/java/android/app/timezone/RulesManager.java b/core/java/android/app/timezone/RulesManager.java index 0a38eb9ae77..dc792569417 100644 --- a/core/java/android/app/timezone/RulesManager.java +++ b/core/java/android/app/timezone/RulesManager.java @@ -68,6 +68,23 @@ public final class RulesManager { private static final String TAG = "timezone.RulesManager"; private static final boolean DEBUG = false; + /** + * The action of the intent that the Android system will broadcast when a time zone rules update + * operation has been successfully staged (i.e. to be applied next reboot) or unstaged. + * + *

See {@link #EXTRA_OPERATION_STAGED} + * + *

This is a protected intent that can only be sent by the system. + */ + public static final String ACTION_RULES_UPDATE_OPERATION = + "com.android.intent.action.timezone.RULES_UPDATE_OPERATION"; + + /** + * The key for a boolean extra for the {@link #ACTION_RULES_UPDATE_OPERATION} intent used to + * indicate whether the operation was a "stage" or an "unstage". + */ + public static final String EXTRA_OPERATION_STAGED = "staged"; + @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = { "SUCCESS", "ERROR_" }, value = { SUCCESS, diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index e5ba6d76578..0bfeae3bd08 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -571,6 +571,10 @@ + + + + diff --git a/services/core/java/com/android/server/timezone/PackageTracker.java b/services/core/java/com/android/server/timezone/PackageTracker.java index 0e8d8bc8e41..8f4cada0d0b 100644 --- a/services/core/java/com/android/server/timezone/PackageTracker.java +++ b/services/core/java/com/android/server/timezone/PackageTracker.java @@ -59,7 +59,7 @@ public class PackageTracker { private static final String TAG = "timezone.PackageTracker"; private final PackageManagerHelper mPackageManagerHelper; - private final IntentHelper mIntentHelper; + private final PackageTrackerIntentHelper mIntentHelper; private final ConfigHelper mConfigHelper; private final PackageStatusStorage mPackageStatusStorage; private final Clock mElapsedRealtimeClock; @@ -103,13 +103,13 @@ public class PackageTracker { helperImpl /* configHelper */, helperImpl /* packageManagerHelper */, new PackageStatusStorage(storageDir), - new IntentHelperImpl(context)); + new PackageTrackerIntentHelperImpl(context)); } // A constructor that can be used by tests to supply mocked / faked dependencies. PackageTracker(Clock elapsedRealtimeClock, ConfigHelper configHelper, PackageManagerHelper packageManagerHelper, PackageStatusStorage packageStatusStorage, - IntentHelper intentHelper) { + PackageTrackerIntentHelper intentHelper) { mElapsedRealtimeClock = elapsedRealtimeClock; mConfigHelper = configHelper; mPackageManagerHelper = packageManagerHelper; diff --git a/services/core/java/com/android/server/timezone/IntentHelper.java b/services/core/java/com/android/server/timezone/PackageTrackerIntentHelper.java similarity index 97% rename from services/core/java/com/android/server/timezone/IntentHelper.java rename to services/core/java/com/android/server/timezone/PackageTrackerIntentHelper.java index 5de54321300..3753ece03bb 100644 --- a/services/core/java/com/android/server/timezone/IntentHelper.java +++ b/services/core/java/com/android/server/timezone/PackageTrackerIntentHelper.java @@ -21,7 +21,7 @@ package com.android.server.timezone; * it is not possible to test various cases with the real one because of the need to simulate * receiving and broadcasting intents. */ -interface IntentHelper { +interface PackageTrackerIntentHelper { void initialize(String updateAppPackageName, String dataAppPackageName, PackageTracker packageTracker); diff --git a/services/core/java/com/android/server/timezone/IntentHelperImpl.java b/services/core/java/com/android/server/timezone/PackageTrackerIntentHelperImpl.java similarity index 93% rename from services/core/java/com/android/server/timezone/IntentHelperImpl.java rename to services/core/java/com/android/server/timezone/PackageTrackerIntentHelperImpl.java index 6e6259d902d..4110d881f3f 100644 --- a/services/core/java/com/android/server/timezone/IntentHelperImpl.java +++ b/services/core/java/com/android/server/timezone/PackageTrackerIntentHelperImpl.java @@ -28,16 +28,16 @@ import android.os.UserHandle; import android.util.Slog; /** - * The bona fide implementation of {@link IntentHelper}. + * The bona fide implementation of {@link PackageTrackerIntentHelper}. */ -final class IntentHelperImpl implements IntentHelper { +final class PackageTrackerIntentHelperImpl implements PackageTrackerIntentHelper { - private final static String TAG = "timezone.IntentHelperImpl"; + private final static String TAG = "timezone.PackageTrackerIntentHelperImpl"; private final Context mContext; private String mUpdaterAppPackageName; - IntentHelperImpl(Context context) { + PackageTrackerIntentHelperImpl(Context context) { mContext = context; } diff --git a/services/core/java/com/android/server/timezone/RulesManagerIntentHelper.java b/services/core/java/com/android/server/timezone/RulesManagerIntentHelper.java new file mode 100644 index 00000000000..bb317cf2f98 --- /dev/null +++ b/services/core/java/com/android/server/timezone/RulesManagerIntentHelper.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2017 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.server.timezone; + +/** + * An easy-to-mock interface around intent sending / receiving for use by + * {@link RulesManagerService}; it is not possible to test various cases with the real one because + * of the need to simulate broadcasting intents. + */ +interface RulesManagerIntentHelper { + + /** + * Send a broadcast informing listeners that a time zone operation is staged. + */ + void sendTimeZoneOperationStaged(); + + /** + * Send a broadcast informing listeners that a time zone operation is no longer staged. + */ + void sendTimeZoneOperationUnstaged(); +} diff --git a/services/core/java/com/android/server/timezone/RulesManagerService.java b/services/core/java/com/android/server/timezone/RulesManagerService.java index be9b204721d..872d7237f4f 100644 --- a/services/core/java/com/android/server/timezone/RulesManagerService.java +++ b/services/core/java/com/android/server/timezone/RulesManagerService.java @@ -99,6 +99,7 @@ public final class RulesManagerService extends IRulesManager.Stub { private final PermissionHelper mPermissionHelper; private final PackageTracker mPackageTracker; private final Executor mExecutor; + private final RulesManagerIntentHelper mIntentHelper; private final TimeZoneDistroInstaller mInstaller; private static RulesManagerService create(Context context) { @@ -106,16 +107,19 @@ public final class RulesManagerService extends IRulesManager.Stub { return new RulesManagerService( helper /* permissionHelper */, helper /* executor */, + helper /* intentHelper */, PackageTracker.create(context), new TimeZoneDistroInstaller(TAG, SYSTEM_TZ_DATA_FILE, TZ_DATA_DIR)); } // A constructor that can be used by tests to supply mocked / faked dependencies. - RulesManagerService(PermissionHelper permissionHelper, - Executor executor, PackageTracker packageTracker, + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + RulesManagerService(PermissionHelper permissionHelper, Executor executor, + RulesManagerIntentHelper intentHelper, PackageTracker packageTracker, TimeZoneDistroInstaller timeZoneDistroInstaller) { mPermissionHelper = permissionHelper; mExecutor = executor; + mIntentHelper = intentHelper; mPackageTracker = packageTracker; mInstaller = timeZoneDistroInstaller; } @@ -271,6 +275,10 @@ public final class RulesManagerService extends IRulesManager.Stub { TimeZoneDistro distro = new TimeZoneDistro(is); int installerResult = mInstaller.stageInstallWithErrorCode(distro); + + // Notify interested parties that something is staged. + sendInstallNotificationIntentIfRequired(installerResult); + int resultCode = mapInstallerResultToApiCode(installerResult); EventLogTags.writeTimezoneInstallComplete(toStringOrNull(mCheckToken), resultCode); sendFinishedStatus(mCallback, resultCode); @@ -291,6 +299,12 @@ public final class RulesManagerService extends IRulesManager.Stub { } } + private void sendInstallNotificationIntentIfRequired(int installerResult) { + if (installerResult == TimeZoneDistroInstaller.INSTALL_SUCCESS) { + mIntentHelper.sendTimeZoneOperationStaged(); + } + } + private int mapInstallerResultToApiCode(int installerResult) { switch (installerResult) { case TimeZoneDistroInstaller.INSTALL_SUCCESS: @@ -351,6 +365,10 @@ public final class RulesManagerService extends IRulesManager.Stub { boolean packageTrackerStatus = false; try { int uninstallResult = mInstaller.stageUninstall(); + + // Notify interested parties that something is staged. + sendUninstallNotificationIntentIfRequired(uninstallResult); + packageTrackerStatus = (uninstallResult == TimeZoneDistroInstaller.UNINSTALL_SUCCESS || uninstallResult == TimeZoneDistroInstaller.UNINSTALL_NOTHING_INSTALLED); @@ -374,6 +392,20 @@ public final class RulesManagerService extends IRulesManager.Stub { mOperationInProgress.set(false); } } + + private void sendUninstallNotificationIntentIfRequired(int uninstallResult) { + switch (uninstallResult) { + case TimeZoneDistroInstaller.UNINSTALL_SUCCESS: + mIntentHelper.sendTimeZoneOperationStaged(); + break; + case TimeZoneDistroInstaller.UNINSTALL_NOTHING_INSTALLED: + mIntentHelper.sendTimeZoneOperationUnstaged(); + break; + case TimeZoneDistroInstaller.UNINSTALL_FAIL: + default: + // No-op - unknown or nothing to notify about. + } + } } private void sendFinishedStatus(ICallback callback, int resultCode) { diff --git a/services/core/java/com/android/server/timezone/RulesManagerServiceHelperImpl.java b/services/core/java/com/android/server/timezone/RulesManagerServiceHelperImpl.java index e8a401e7923..8f5c7a78330 100644 --- a/services/core/java/com/android/server/timezone/RulesManagerServiceHelperImpl.java +++ b/services/core/java/com/android/server/timezone/RulesManagerServiceHelperImpl.java @@ -18,22 +18,20 @@ package com.android.server.timezone; import com.android.internal.util.DumpUtils; +import android.app.timezone.RulesManager; import android.content.Context; -import android.content.pm.PackageManager; +import android.content.Intent; import android.os.AsyncTask; -import android.os.Binder; -import android.os.ParcelFileDescriptor; +import android.os.UserHandle; -import java.io.FileInputStream; -import java.io.IOException; import java.io.PrintWriter; import java.util.concurrent.Executor; -import libcore.io.Streams; /** * A single class that implements multiple helper interfaces for use by {@link RulesManagerService}. */ -final class RulesManagerServiceHelperImpl implements PermissionHelper, Executor { +final class RulesManagerServiceHelperImpl + implements PermissionHelper, Executor, RulesManagerIntentHelper { private final Context mContext; @@ -55,4 +53,22 @@ final class RulesManagerServiceHelperImpl implements PermissionHelper, Executor public void execute(Runnable runnable) { AsyncTask.execute(runnable); } + + @Override + public void sendTimeZoneOperationStaged() { + sendOperationIntent(true /* staged */); + } + + @Override + public void sendTimeZoneOperationUnstaged() { + sendOperationIntent(false /* staged */); + } + + private void sendOperationIntent(boolean staged) { + Intent intent = new Intent(RulesManager.ACTION_RULES_UPDATE_OPERATION); + intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); + intent.putExtra(RulesManager.EXTRA_OPERATION_STAGED, staged); + mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM); + } + } diff --git a/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java b/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java index 9cf6392cab9..d9f4adfb5e0 100644 --- a/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java +++ b/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java @@ -31,7 +31,6 @@ import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; import java.io.File; -import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.time.Clock; @@ -1400,7 +1399,7 @@ public class PackageTrackerTest { /** * A fake IntentHelper implementation for use in tests. */ - private static class FakeIntentHelper implements IntentHelper { + private static class FakeIntentHelper implements PackageTrackerIntentHelper { private PackageTracker mPackageTracker; private String mUpdateAppPackageName; diff --git a/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java index 1cfae1ef1a6..f5969f39e87 100644 --- a/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java @@ -68,6 +68,7 @@ public class RulesManagerServiceTest { private FakeExecutor mFakeExecutor; private PermissionHelper mMockPermissionHelper; + private RulesManagerIntentHelper mMockIntentHelper; private PackageTracker mMockPackageTracker; private TimeZoneDistroInstaller mMockTimeZoneDistroInstaller; @@ -77,11 +78,13 @@ public class RulesManagerServiceTest { mMockPackageTracker = mock(PackageTracker.class); mMockPermissionHelper = mock(PermissionHelper.class); + mMockIntentHelper = mock(RulesManagerIntentHelper.class); mMockTimeZoneDistroInstaller = mock(TimeZoneDistroInstaller.class); mRulesManagerService = new RulesManagerService( mMockPermissionHelper, mFakeExecutor, + mMockIntentHelper, mMockPackageTracker, mMockTimeZoneDistroInstaller); } @@ -329,6 +332,7 @@ public class RulesManagerServiceTest { mFakeExecutor.assertNothingQueued(); verifyNoInstallerCallsMade(); verifyNoPackageTrackerCallsMade(); + verifyNoIntentsSent(); } @Test @@ -353,6 +357,7 @@ public class RulesManagerServiceTest { mFakeExecutor.assertNothingQueued(); verifyNoInstallerCallsMade(); verifyNoPackageTrackerCallsMade(); + verifyNoIntentsSent(); } @Test @@ -372,6 +377,7 @@ public class RulesManagerServiceTest { mFakeExecutor.assertNothingQueued(); verifyNoInstallerCallsMade(); verifyNoPackageTrackerCallsMade(); + verifyNoIntentsSent(); } @Test @@ -394,6 +400,7 @@ public class RulesManagerServiceTest { mFakeExecutor.assertNothingQueued(); verifyNoInstallerCallsMade(); verifyNoPackageTrackerCallsMade(); + verifyNoIntentsSent(); } @Test @@ -416,6 +423,7 @@ public class RulesManagerServiceTest { callback.assertNoResultReceived(); verifyNoInstallerCallsMade(); verifyNoPackageTrackerCallsMade(); + verifyNoIntentsSent(); // Set up the installer. configureStageInstallExpectation(TimeZoneDistroInstaller.INSTALL_SUCCESS); @@ -428,6 +436,7 @@ public class RulesManagerServiceTest { // Verify the expected calls were made to other components. verifyStageInstallCalled(); verifyPackageTrackerCalled(token, true /* success */); + verifyStagedOperationIntentSent(); // Check the callback was called. callback.assertResultReceived(Callback.SUCCESS); @@ -450,6 +459,7 @@ public class RulesManagerServiceTest { // Assert nothing has happened yet. verifyNoInstallerCallsMade(); callback.assertNoResultReceived(); + verifyNoIntentsSent(); // Set up the installer. configureStageInstallExpectation(TimeZoneDistroInstaller.INSTALL_SUCCESS); @@ -462,6 +472,7 @@ public class RulesManagerServiceTest { // Verify the expected calls were made to other components. verifyStageInstallCalled(); verifyPackageTrackerCalled(null /* expectedToken */, true /* success */); + verifyStagedOperationIntentSent(); // Check the callback was received. callback.assertResultReceived(Callback.SUCCESS); @@ -486,6 +497,7 @@ public class RulesManagerServiceTest { // Assert nothing has happened yet. verifyNoInstallerCallsMade(); callback.assertNoResultReceived(); + verifyNoIntentsSent(); // Set up the installer. configureStageInstallExpectation(TimeZoneDistroInstaller.INSTALL_FAIL_VALIDATION_ERROR); @@ -502,6 +514,9 @@ public class RulesManagerServiceTest { boolean expectedSuccess = true; verifyPackageTrackerCalled(token, expectedSuccess); + // Nothing should be staged, so no intents sent. + verifyNoIntentsSent(); + // Check the callback was received. callback.assertResultReceived(Callback.ERROR_INSTALL_VALIDATION_ERROR); } @@ -529,6 +544,7 @@ public class RulesManagerServiceTest { mFakeExecutor.assertNothingQueued(); verifyNoInstallerCallsMade(); verifyNoPackageTrackerCallsMade(); + verifyNoIntentsSent(); } @Test @@ -548,6 +564,7 @@ public class RulesManagerServiceTest { mFakeExecutor.assertNothingQueued(); verifyNoInstallerCallsMade(); verifyNoPackageTrackerCallsMade(); + verifyNoIntentsSent(); } @Test @@ -566,6 +583,7 @@ public class RulesManagerServiceTest { mFakeExecutor.assertNothingQueued(); verifyNoInstallerCallsMade(); verifyNoPackageTrackerCallsMade(); + verifyNoIntentsSent(); } @Test @@ -585,6 +603,7 @@ public class RulesManagerServiceTest { callback.assertNoResultReceived(); verifyNoInstallerCallsMade(); verifyNoPackageTrackerCallsMade(); + verifyNoIntentsSent(); // Set up the installer. configureStageUninstallExpectation(TimeZoneDistroInstaller.UNINSTALL_SUCCESS); @@ -595,6 +614,7 @@ public class RulesManagerServiceTest { // Verify the expected calls were made to other components. verifyStageUninstallCalled(); verifyPackageTrackerCalled(token, true /* success */); + verifyStagedOperationIntentSent(); // Check the callback was called. callback.assertResultReceived(Callback.SUCCESS); @@ -617,6 +637,7 @@ public class RulesManagerServiceTest { callback.assertNoResultReceived(); verifyNoInstallerCallsMade(); verifyNoPackageTrackerCallsMade(); + verifyNoIntentsSent(); // Set up the installer. configureStageUninstallExpectation(TimeZoneDistroInstaller.UNINSTALL_NOTHING_INSTALLED); @@ -627,6 +648,7 @@ public class RulesManagerServiceTest { // Verify the expected calls were made to other components. verifyStageUninstallCalled(); verifyPackageTrackerCalled(token, true /* success */); + verifyUnstagedOperationIntentSent(); // Check the callback was called. callback.assertResultReceived(Callback.SUCCESS); @@ -645,6 +667,7 @@ public class RulesManagerServiceTest { // Assert nothing has happened yet. verifyNoInstallerCallsMade(); callback.assertNoResultReceived(); + verifyNoIntentsSent(); // Set up the installer. configureStageUninstallExpectation(TimeZoneDistroInstaller.UNINSTALL_SUCCESS); @@ -655,6 +678,7 @@ public class RulesManagerServiceTest { // Verify the expected calls were made to other components. verifyStageUninstallCalled(); verifyPackageTrackerCalled(null /* expectedToken */, true /* success */); + verifyStagedOperationIntentSent(); // Check the callback was received. callback.assertResultReceived(Callback.SUCCESS); @@ -676,6 +700,7 @@ public class RulesManagerServiceTest { // Assert nothing has happened yet. verifyNoInstallerCallsMade(); callback.assertNoResultReceived(); + verifyNoIntentsSent(); // Set up the installer. configureStageUninstallExpectation(TimeZoneDistroInstaller.UNINSTALL_FAIL); @@ -686,6 +711,7 @@ public class RulesManagerServiceTest { // Verify the expected calls were made to other components. verifyStageUninstallCalled(); verifyPackageTrackerCalled(token, false /* success */); + verifyNoIntentsSent(); // Check the callback was received. callback.assertResultReceived(Callback.ERROR_UNKNOWN_FAILURE); @@ -714,6 +740,7 @@ public class RulesManagerServiceTest { // Verify the expected calls were made to other components. verifyPackageTrackerCalled(token, true /* success */); verifyNoInstallerCallsMade(); + verifyNoIntentsSent(); } @Test @@ -734,6 +761,7 @@ public class RulesManagerServiceTest { // Assert no other calls were made. verifyNoInstallerCallsMade(); verifyNoPackageTrackerCallsMade(); + verifyNoIntentsSent(); } @Test @@ -749,6 +777,7 @@ public class RulesManagerServiceTest { // Assert everything required was done. verifyNoInstallerCallsMade(); verifyPackageTrackerCalled(token, false /* success */); + verifyNoIntentsSent(); } @Test @@ -761,6 +790,7 @@ public class RulesManagerServiceTest { // Assert everything required was done. verifyNoInstallerCallsMade(); verifyPackageTrackerCalled(null /* token */, true /* success */); + verifyNoIntentsSent(); } @Test @@ -865,6 +895,21 @@ public class RulesManagerServiceTest { reset(mMockPackageTracker); } + private void verifyNoIntentsSent() { + verifyNoMoreInteractions(mMockIntentHelper); + reset(mMockIntentHelper); + } + + private void verifyStagedOperationIntentSent() { + verify(mMockIntentHelper).sendTimeZoneOperationStaged(); + reset(mMockIntentHelper); + } + + private void verifyUnstagedOperationIntentSent() { + verify(mMockIntentHelper).sendTimeZoneOperationUnstaged(); + reset(mMockIntentHelper); + } + private void configureCallerHasPermission() throws Exception { doNothing() .when(mMockPermissionHelper) -- GitLab From 16d42def254af0bc821a73c0ce4d821a426d157a Mon Sep 17 00:00:00 2001 From: Emilie Roberts Date: Wed, 31 Jan 2018 16:46:46 +0000 Subject: [PATCH 013/671] Remove ESC key fallback mapping The escape key has a fallback "back" behaviour. On ChromeOS or tablets with bluetooth/attached keyboards, pressing the ESC key can unexpectedly close applications. This removes the fallback mapping so that ESC does nothing. Bug: 71907807 Test: Manual testing on Marlin Change-Id: I747f0bd743ec117e6ae47fae527600a3ab5690ba --- data/keyboards/Generic.kcm | 2 +- data/keyboards/Virtual.kcm | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data/keyboards/Generic.kcm b/data/keyboards/Generic.kcm index 1ef74ba5e1a..87e7add960e 100644 --- a/data/keyboards/Generic.kcm +++ b/data/keyboards/Generic.kcm @@ -472,7 +472,7 @@ key PLUS { ### Non-printing keys ### key ESCAPE { - base: fallback BACK + base: none alt, meta: fallback HOME ctrl: fallback MENU } diff --git a/data/keyboards/Virtual.kcm b/data/keyboards/Virtual.kcm index c4647e04f3c..f05631ce324 100644 --- a/data/keyboards/Virtual.kcm +++ b/data/keyboards/Virtual.kcm @@ -469,7 +469,7 @@ key PLUS { ### Non-printing keys ### key ESCAPE { - base: fallback BACK + base: none alt, meta: fallback HOME ctrl: fallback MENU } -- GitLab From 3abea7fe3f15ae45f0fc986fbb6d48414498f14b Mon Sep 17 00:00:00 2001 From: Emilie Roberts Date: Wed, 31 Jan 2018 15:52:16 +0000 Subject: [PATCH 014/671] Add Ctrl-Alt-Backspace to Back mapping Android devices with a physical keyboard connected may not have a way to execute the "Back Behaviour" without using a touchscreen or pointer. This adds a ctrl-alt-backspace mapping to the back behaviour, similar to TalkBalk. Use cases include Pixel C, accessibility situations where touchscreens are not convenient or feasible, or other phones/tablets with a hardware keyboard attached and keyboard only interaction is desired. Previous to http://ag/3540362, ESC provided this functionality. Bug: 71907807 Test: Manual testing on Marlin Change-Id: I5015a17add26824a40e5eac1bced8e9ca7b98efa --- data/keyboards/Generic.kcm | 4 ++++ data/keyboards/Virtual.kcm | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/data/keyboards/Generic.kcm b/data/keyboards/Generic.kcm index 1ef74ba5e1a..4184d3e647d 100644 --- a/data/keyboards/Generic.kcm +++ b/data/keyboards/Generic.kcm @@ -477,6 +477,10 @@ key ESCAPE { ctrl: fallback MENU } +key DEL { + ctrl+alt: fallback BACK +} + ### Gamepad buttons ### key BUTTON_A { diff --git a/data/keyboards/Virtual.kcm b/data/keyboards/Virtual.kcm index c4647e04f3c..1d05ab30583 100644 --- a/data/keyboards/Virtual.kcm +++ b/data/keyboards/Virtual.kcm @@ -474,6 +474,10 @@ key ESCAPE { ctrl: fallback MENU } +key DEL { + ctrl+alt: fallback BACK +} + ### Gamepad buttons ### key BUTTON_A { -- GitLab From c3e6cba244b34f05178aa8b6aee18e577463bb46 Mon Sep 17 00:00:00 2001 From: Michael Plass Date: Wed, 31 Jan 2018 11:00:43 -0800 Subject: [PATCH 015/671] Remove wifi/ScanResult.averageRssi method This logic belongs in the wifi service, not here. Bug: 68401944 Test: Unit tests Change-Id: I22f07254b0f52b4b88f809a624180bab1447e34b --- wifi/java/android/net/wifi/ScanResult.java | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java index c46789cab50..8024bf08b1d 100644 --- a/wifi/java/android/net/wifi/ScanResult.java +++ b/wifi/java/android/net/wifi/ScanResult.java @@ -272,27 +272,6 @@ public class ScanResult implements Parcelable { */ public RadioChainInfo[] radioChainInfos; - /** - * @hide - * Update RSSI of the scan result - * @param previousRssi - * @param previousSeen - * @param maxAge - */ - public void averageRssi(int previousRssi, long previousSeen, int maxAge) { - - if (seen == 0) { - seen = System.currentTimeMillis(); - } - long age = seen - previousSeen; - - if (previousSeen > 0 && age > 0 && age < maxAge/2) { - // Average the RSSI with previously seen instances of this scan result - double alpha = 0.5 - (double) age / (double) maxAge; - level = (int) ((double) level * (1 - alpha) + (double) previousRssi * alpha); - } - } - /** * Status indicating the scan result does not correspond to a user's saved configuration * @hide -- GitLab From b6ec1be4922043b0f8031f542f1e440d5c2f08bb Mon Sep 17 00:00:00 2001 From: Vladislav Kaznacheev Date: Wed, 31 Jan 2018 16:13:58 -0800 Subject: [PATCH 016/671] [DO NOT MERGE] Fix context menu position for RTL Based on https://android-review.googlesource.com/574843. Added APCT coverage to verify the fix and prevent regressions. Bug: 70920189 Test: android.view.menu.ContextMenuTest Change-Id: Id9ee500751fe6f3da07bf10fb510ac49487104d0 --- .../internal/view/menu/MenuPopupHelper.java | 2 +- .../internal/view/menu/StandardMenuPopup.java | 12 +- core/tests/coretests/AndroidManifest.xml | 7 ++ .../coretests/res/layout/context_menu.xml | 54 +++++++++ .../src/android/util/PollingCheck.java | 104 ++++++++++++++++++ .../view/menu/ContextMenuActivity.java | 54 +++++++++ .../android/view/menu/ContextMenuTest.java | 93 ++++++++++++++++ .../widget/espresso/ContextMenuUtils.java | 90 ++++++++++++++- 8 files changed, 407 insertions(+), 9 deletions(-) create mode 100644 core/tests/coretests/res/layout/context_menu.xml create mode 100644 core/tests/coretests/src/android/util/PollingCheck.java create mode 100644 core/tests/coretests/src/android/view/menu/ContextMenuActivity.java create mode 100644 core/tests/coretests/src/android/view/menu/ContextMenuTest.java diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java index 6af41a51f0d..324f923674e 100644 --- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java +++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java @@ -256,7 +256,7 @@ public class MenuPopupHelper implements MenuHelper { final int hgrav = Gravity.getAbsoluteGravity(mDropDownGravity, mAnchorView.getLayoutDirection()) & Gravity.HORIZONTAL_GRAVITY_MASK; if (hgrav == Gravity.RIGHT) { - xOffset += mAnchorView.getWidth(); + xOffset -= mAnchorView.getWidth(); } popup.setHorizontalOffset(xOffset); diff --git a/core/java/com/android/internal/view/menu/StandardMenuPopup.java b/core/java/com/android/internal/view/menu/StandardMenuPopup.java index d9ca5be0502..445379b1d9f 100644 --- a/core/java/com/android/internal/view/menu/StandardMenuPopup.java +++ b/core/java/com/android/internal/view/menu/StandardMenuPopup.java @@ -263,7 +263,6 @@ final class StandardMenuPopup extends MenuPopup implements OnDismissListener, On mShownAnchorView, mOverflowOnly, mPopupStyleAttr, mPopupStyleRes); subPopup.setPresenterCallback(mPresenterCallback); subPopup.setForceShowIcon(MenuPopup.shouldPreserveIconSpacing(subMenu)); - subPopup.setGravity(mDropDownGravity); // Pass responsibility for handling onDismiss to the submenu. subPopup.setOnDismissListener(mOnDismissListener); @@ -273,8 +272,17 @@ final class StandardMenuPopup extends MenuPopup implements OnDismissListener, On mMenu.close(false /* closeAllMenus */); // Show the new sub-menu popup at the same location as this popup. - final int horizontalOffset = mPopup.getHorizontalOffset(); + int horizontalOffset = mPopup.getHorizontalOffset(); final int verticalOffset = mPopup.getVerticalOffset(); + + // As xOffset of parent menu popup is subtracted with Anchor width for Gravity.RIGHT, + // So, again to display sub-menu popup in same xOffset, add the Anchor width. + final int hgrav = Gravity.getAbsoluteGravity(mDropDownGravity, + mAnchorView.getLayoutDirection()) & Gravity.HORIZONTAL_GRAVITY_MASK; + if (hgrav == Gravity.RIGHT) { + horizontalOffset += mAnchorView.getWidth(); + } + if (subPopup.tryShow(horizontalOffset, verticalOffset)) { if (mPresenterCallback != null) { mPresenterCallback.onOpenSubMenu(subMenu); diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml index ab9912a438d..c0a8acda628 100644 --- a/core/tests/coretests/AndroidManifest.xml +++ b/core/tests/coretests/AndroidManifest.xml @@ -1041,6 +1041,13 @@ + + + + + + + diff --git a/core/tests/coretests/res/layout/context_menu.xml b/core/tests/coretests/res/layout/context_menu.xml new file mode 100644 index 00000000000..3b9e2bdb313 --- /dev/null +++ b/core/tests/coretests/res/layout/context_menu.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + diff --git a/core/tests/coretests/src/android/util/PollingCheck.java b/core/tests/coretests/src/android/util/PollingCheck.java new file mode 100644 index 00000000000..468b9b2a486 --- /dev/null +++ b/core/tests/coretests/src/android/util/PollingCheck.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.util; + +import org.junit.Assert; + +/** + * Utility used for testing that allows to poll for a certain condition to happen within a timeout. + * + * Code copied from com.android.compatibility.common.util.PollingCheck + */ +public abstract class PollingCheck { + + private static final long DEFAULT_TIMEOUT = 3000; + private static final long TIME_SLICE = 50; + private final long mTimeout; + + /** + * The condition that the PollingCheck should use to proceed successfully. + */ + public interface PollingCheckCondition { + + /** + * @return Whether the polling condition has been met. + */ + boolean canProceed(); + } + + public PollingCheck(long timeout) { + mTimeout = timeout; + } + + protected abstract boolean check(); + + /** + * Start running the polling check. + */ + public void run() { + if (check()) { + return; + } + + long timeout = mTimeout; + while (timeout > 0) { + try { + Thread.sleep(TIME_SLICE); + } catch (InterruptedException e) { + Assert.fail("unexpected InterruptedException"); + } + + if (check()) { + return; + } + + timeout -= TIME_SLICE; + } + + Assert.fail("unexpected timeout"); + } + + /** + * Instantiate and start polling for a given condition with a default 3000ms timeout. + * + * @param condition The condition to check for success. + */ + public static void waitFor(final PollingCheckCondition condition) { + new PollingCheck(DEFAULT_TIMEOUT) { + @Override + protected boolean check() { + return condition.canProceed(); + } + }.run(); + } + + /** + * Instantiate and start polling for a given condition. + * + * @param timeout Time out in ms + * @param condition The condition to check for success. + */ + public static void waitFor(long timeout, final PollingCheckCondition condition) { + new PollingCheck(timeout) { + @Override + protected boolean check() { + return condition.canProceed(); + } + }.run(); + } +} + diff --git a/core/tests/coretests/src/android/view/menu/ContextMenuActivity.java b/core/tests/coretests/src/android/view/menu/ContextMenuActivity.java new file mode 100644 index 00000000000..830b3d54977 --- /dev/null +++ b/core/tests/coretests/src/android/view/menu/ContextMenuActivity.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view.menu; + +import android.app.Activity; +import android.os.Bundle; +import android.view.ContextMenu; +import android.view.ContextMenu.ContextMenuInfo; +import android.view.View; + +import com.android.frameworks.coretests.R; + +public class ContextMenuActivity extends Activity { + + static final String LABEL_ITEM = "Item"; + static final String LABEL_SUBMENU = "Submenu"; + static final String LABEL_SUBITEM = "Subitem"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.context_menu); + registerForContextMenu(getTargetLtr()); + registerForContextMenu(getTargetRtl()); + } + + @Override + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { + menu.add(LABEL_ITEM); + menu.addSubMenu(LABEL_SUBMENU).add(LABEL_SUBITEM); + } + + View getTargetLtr() { + return findViewById(R.id.context_menu_target_ltr); + } + + View getTargetRtl() { + return findViewById(R.id.context_menu_target_rtl); + } +} diff --git a/core/tests/coretests/src/android/view/menu/ContextMenuTest.java b/core/tests/coretests/src/android/view/menu/ContextMenuTest.java new file mode 100644 index 00000000000..59d4e55d8d4 --- /dev/null +++ b/core/tests/coretests/src/android/view/menu/ContextMenuTest.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view.menu; + +import android.content.Context; +import android.graphics.Point; +import android.support.test.filters.MediumTest; +import android.test.ActivityInstrumentationTestCase; +import android.util.PollingCheck; +import android.view.Display; +import android.view.View; +import android.view.WindowManager; +import android.widget.espresso.ContextMenuUtils; + +@MediumTest +public class ContextMenuTest extends ActivityInstrumentationTestCase { + + public ContextMenuTest() { + super("com.android.frameworks.coretests", ContextMenuActivity.class); + } + + public void testContextMenuPositionLtr() throws InterruptedException { + testMenuPosition(getActivity().getTargetLtr()); + } + + public void testContextMenuPositionRtl() throws InterruptedException { + testMenuPosition(getActivity().getTargetRtl()); + } + + private void testMenuPosition(View target) throws InterruptedException { + final int minScreenDimension = getMinScreenDimension(); + if (minScreenDimension < 320) { + // Assume there is insufficient room for the context menu to be aligned properly. + return; + } + + int offsetX = target.getWidth() / 2; + int offsetY = target.getHeight() / 2; + + getInstrumentation().runOnMainSync(() -> target.performLongClick(offsetX, offsetY)); + + PollingCheck.waitFor( + () -> ContextMenuUtils.isMenuItemClickable(ContextMenuActivity.LABEL_SUBMENU)); + + ContextMenuUtils.assertContextMenuAlignment(target, offsetX, offsetY); + + ContextMenuUtils.clickMenuItem(ContextMenuActivity.LABEL_SUBMENU); + + PollingCheck.waitFor( + () -> ContextMenuUtils.isMenuItemClickable(ContextMenuActivity.LABEL_SUBITEM)); + + if (minScreenDimension < getCascadingMenuTreshold()) { + // A non-cascading submenu should be displayed at the same location as its parent. + // Not testing cascading submenu position, as it is positioned differently. + ContextMenuUtils.assertContextMenuAlignment(target, offsetX, offsetY); + } + } + + /** + * Returns the minimum of the default display's width and height. + */ + private int getMinScreenDimension() { + final WindowManager windowManager = (WindowManager) getActivity().getSystemService( + Context.WINDOW_SERVICE); + final Display display = windowManager.getDefaultDisplay(); + final Point displaySize = new Point(); + display.getRealSize(displaySize); + return Math.min(displaySize.x, displaySize.y); + } + + /** + * Returns the minimum display size where cascading submenus are supported. + */ + private int getCascadingMenuTreshold() { + // Use the same dimension resource as in MenuPopupHelper.createPopup(). + return getActivity().getResources().getDimensionPixelSize( + com.android.internal.R.dimen.cascading_menus_min_smallest_width); + } +} diff --git a/core/tests/coretests/src/android/widget/espresso/ContextMenuUtils.java b/core/tests/coretests/src/android/widget/espresso/ContextMenuUtils.java index c8218aa490f..487a881082e 100644 --- a/core/tests/coretests/src/android/widget/espresso/ContextMenuUtils.java +++ b/core/tests/coretests/src/android/widget/espresso/ContextMenuUtils.java @@ -17,25 +17,32 @@ package android.widget.espresso; import static android.support.test.espresso.Espresso.onView; +import static android.support.test.espresso.action.ViewActions.click; import static android.support.test.espresso.assertion.ViewAssertions.matches; import static android.support.test.espresso.matcher.RootMatchers.withDecorView; import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant; import static android.support.test.espresso.matcher.ViewMatchers.hasFocus; import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom; import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; +import static android.support.test.espresso.matcher.ViewMatchers.isDisplayingAtLeast; import static android.support.test.espresso.matcher.ViewMatchers.isEnabled; import static android.support.test.espresso.matcher.ViewMatchers.withText; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.not; -import com.android.internal.view.menu.ListMenuItemView; - import android.support.test.espresso.NoMatchingRootException; import android.support.test.espresso.NoMatchingViewException; import android.support.test.espresso.ViewInteraction; import android.support.test.espresso.matcher.ViewMatchers; +import android.view.View; import android.widget.MenuPopupWindow.MenuDropDownListView; +import com.android.internal.view.menu.ListMenuItemView; + +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.TypeSafeMatcher; + /** * Espresso utility methods for the context menu. */ @@ -82,10 +89,15 @@ public final class ContextMenuUtils { private static void asssertContextMenuContainsItemWithEnabledState(String itemLabel, boolean enabled) { onContextMenu().check(matches( - hasDescendant(allOf( - isAssignableFrom(ListMenuItemView.class), - enabled ? isEnabled() : not(isEnabled()), - hasDescendant(withText(itemLabel)))))); + hasDescendant(getVisibleMenuItemMatcher(itemLabel, enabled)))); + } + + private static Matcher getVisibleMenuItemMatcher(String itemLabel, boolean enabled) { + return allOf( + isAssignableFrom(ListMenuItemView.class), + hasDescendant(withText(itemLabel)), + enabled ? isEnabled() : not(isEnabled()), + isDisplayingAtLeast(90)); } /** @@ -107,4 +119,70 @@ public final class ContextMenuUtils { public static void assertContextMenuContainsItemDisabled(String itemLabel) { asssertContextMenuContainsItemWithEnabledState(itemLabel, false); } + + /** + * Asserts that the context menu window is aligned to a given view with a given offset. + * + * @param anchor Anchor view. + * @param offsetX x offset + * @param offsetY y offset. + * @throws AssertionError if the assertion fails + */ + public static void assertContextMenuAlignment(View anchor, int offsetX, int offsetY) { + int [] expectedLocation = new int[2]; + anchor.getLocationOnScreen(expectedLocation); + expectedLocation[0] += offsetX; + expectedLocation[1] += offsetY; + + final boolean rtl = anchor.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; + + onContextMenu().check(matches(new TypeSafeMatcher() { + @Override + public void describeTo(Description description) { + description.appendText("root view "); + description.appendText(rtl ? "right" : "left"); + description.appendText("="); + description.appendText(Integer.toString(offsetX)); + description.appendText(", top="); + description.appendText(Integer.toString(offsetY)); + } + + @Override + public boolean matchesSafely(View view) { + View rootView = view.getRootView(); + int [] actualLocation = new int[2]; + rootView.getLocationOnScreen(actualLocation); + if (rtl) { + actualLocation[0] += rootView.getWidth(); + } + return expectedLocation[0] == actualLocation[0] + && expectedLocation[1] == actualLocation[1]; + } + })); + } + + /** + * Check is the menu item is clickable (i.e. visible and enabled). + * + * @param itemLabel Label of the item. + * @return True if the menu item is clickable. + */ + public static boolean isMenuItemClickable(String itemLabel) { + try { + onContextMenu().check(matches( + hasDescendant(getVisibleMenuItemMatcher(itemLabel, true)))); + return true; + } catch (NoMatchingRootException | NoMatchingViewException | AssertionError e) { + return false; + } + } + + /** + * Click on a menu item with the specified label + * @param itemLabel Label of the item. + */ + public static void clickMenuItem(String itemLabel) { + onView(getVisibleMenuItemMatcher(itemLabel, true)) + .inRoot(withDecorView(hasFocus())).perform(click()); + } } -- GitLab From 4b1e6c3b2fcef7dae2afe869a77b7f0b5f79f4ba Mon Sep 17 00:00:00 2001 From: Siyuan Zhou Date: Wed, 20 Dec 2017 11:38:12 -0800 Subject: [PATCH 017/671] BootReceiver: changed deprecated Build.RADIO to Build.getRadioVerison to get non-empty radio firmware version in SYSTEM_LAST_KMSG. Test: Manually verified on Pixel 2 XL, Nexus 5X and 6P devices. BUG:70934228 Change-Id: I40da7e659619e06587409c6b83c655b46bcbd5b2 --- core/java/com/android/server/BootReceiver.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/java/com/android/server/BootReceiver.java b/core/java/com/android/server/BootReceiver.java index 8848e393900..87fde4128ec 100644 --- a/core/java/com/android/server/BootReceiver.java +++ b/core/java/com/android/server/BootReceiver.java @@ -161,7 +161,7 @@ public class BootReceiver extends BroadcastReceiver { .append("Revision: ") .append(SystemProperties.get("ro.revision", "")).append("\n") .append("Bootloader: ").append(Build.BOOTLOADER).append("\n") - .append("Radio: ").append(Build.RADIO).append("\n") + .append("Radio: ").append(Build.getRadioVersion()).append("\n") .append("Kernel: ") .append(FileUtils.readTextFile(new File("/proc/version"), 1024, "...\n")) .append("\n").toString(); -- GitLab From ca575236145d1c739c2b17ba4abc95c0e2c029bc Mon Sep 17 00:00:00 2001 From: Evan Rosky Date: Thu, 26 Oct 2017 12:50:33 -0700 Subject: [PATCH 018/671] [DO NOT MERGE] Fix some mouse + list-item selection/scrolling issues Any "touch" interaction now hides selection (since mouse doesn't enable touch-mode, this wasn't happening anymore; also, allowing listview to scroll an actual selection off-screen would be very involved and risky). The selector hilight remains (since mouse doesn't enter touch-mode). This is a new scenario, so this change also makes sure to hide the selector hilight when it's target view is scrolled off-screen. Bug: 67881712 Bug: 67720587 Test: Existing CTS tests still pass. Can now click list items via mouse even when scrolled. Visually checked that that selector hilight is not visible if off-screen. Change-Id: Ia7b0fd7b247e8d9d9e609364a5500717df648fd9 --- core/java/android/widget/AbsListView.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 5476ab216f2..4ee92b3c320 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -3873,6 +3873,7 @@ public abstract class AbsListView extends AdapterView implements Te private void onTouchDown(MotionEvent ev) { mHasPerformedLongPress = false; mActivePointerId = ev.getPointerId(0); + hideSelector(); if (mTouchMode == TOUCH_MODE_OVERFLING) { // Stopped the fling. It is a scroll. @@ -5233,17 +5234,21 @@ public abstract class AbsListView extends AdapterView implements Te } mRecycler.fullyDetachScrapViews(); + boolean selectorOnScreen = false; if (!inTouchMode && mSelectedPosition != INVALID_POSITION) { final int childIndex = mSelectedPosition - mFirstPosition; if (childIndex >= 0 && childIndex < getChildCount()) { positionSelector(mSelectedPosition, getChildAt(childIndex)); + selectorOnScreen = true; } } else if (mSelectorPosition != INVALID_POSITION) { final int childIndex = mSelectorPosition - mFirstPosition; if (childIndex >= 0 && childIndex < getChildCount()) { - positionSelector(INVALID_POSITION, getChildAt(childIndex)); + positionSelector(mSelectorPosition, getChildAt(childIndex)); + selectorOnScreen = true; } - } else { + } + if (!selectorOnScreen) { mSelectorRect.setEmpty(); } -- GitLab From 5de17526b2cfea0e5ddccd8eff663f48f154c738 Mon Sep 17 00:00:00 2001 From: Leon Scroggins III Date: Wed, 31 Jan 2018 11:10:40 -0500 Subject: [PATCH 019/671] Deprecate createFromResourceStream with BitmapFactory.Options Bug: 63909536 Test: none This version is never used internally with a non-null Options object. The Options object prevents us from taking advantage of the new ImageDecoder. Change-Id: I867f482249a0a6f4b37220b597ef38abf0684360 --- api/current.txt | 2 +- graphics/java/android/graphics/drawable/Drawable.java | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/api/current.txt b/api/current.txt index b18115eb38f..b61f8037402 100644 --- a/api/current.txt +++ b/api/current.txt @@ -14602,7 +14602,7 @@ package android.graphics.drawable { method public final android.graphics.Rect copyBounds(); method public static android.graphics.drawable.Drawable createFromPath(java.lang.String); method public static android.graphics.drawable.Drawable createFromResourceStream(android.content.res.Resources, android.util.TypedValue, java.io.InputStream, java.lang.String); - method public static android.graphics.drawable.Drawable createFromResourceStream(android.content.res.Resources, android.util.TypedValue, java.io.InputStream, java.lang.String, android.graphics.BitmapFactory.Options); + method public static deprecated android.graphics.drawable.Drawable createFromResourceStream(android.content.res.Resources, android.util.TypedValue, java.io.InputStream, java.lang.String, android.graphics.BitmapFactory.Options); method public static android.graphics.drawable.Drawable createFromStream(java.io.InputStream, java.lang.String); method public static android.graphics.drawable.Drawable createFromXml(android.content.res.Resources, org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; method public static android.graphics.drawable.Drawable createFromXml(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java index f17cd768c38..a076d8134bb 100644 --- a/graphics/java/android/graphics/drawable/Drawable.java +++ b/graphics/java/android/graphics/drawable/Drawable.java @@ -1168,6 +1168,8 @@ public abstract class Drawable { /** * Create a drawable from an inputstream, using the given resources and * value to determine density information. + * + * @deprecated Prefer the version without an Options object. */ public static Drawable createFromResourceStream(Resources res, TypedValue value, InputStream is, String srcName, BitmapFactory.Options opts) { -- GitLab From 85ecae59b16d613426cdbee8ae592bed009f701a Mon Sep 17 00:00:00 2001 From: "Torne (Richard Coles)" Date: Tue, 30 Jan 2018 18:13:00 -0500 Subject: [PATCH 020/671] Don't attempt to preload fonts in isolated processes. Isolated processes used to run external services have the package name of the application which caused them to be started (i.e. the client application bound to the service) in their ApplicationInfo's packageName field, not the package name which is actually loaded in the current process. This meant that the font preloading code used the manifest of the client application to determine the resource ID for the preloaded font list, but then looked up that resource ID in the service's APK, which typically results in a crash as that resource ID is unlikely to exist. Avoid this case occurring by not doing font preloading in isolated processes, which are not normally capable of displaying UI in any case and so likely do not require it. Bug: 70968451 Test: CtsWebkitTestCases Change-Id: Id47e01aab28a6fd48f5928ce33d5060fb2717527 --- core/java/android/app/ActivityThread.java | 28 ++++++++++++----------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 29e091b0f26..5ddc03d7830 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -5874,21 +5874,23 @@ public final class ActivityThread extends ClientTransactionHandler { // Preload fonts resources FontsContract.setApplicationContextForResources(appContext); - try { - final ApplicationInfo info = - getPackageManager().getApplicationInfo( - data.appInfo.packageName, - PackageManager.GET_META_DATA /*flags*/, - UserHandle.myUserId()); - if (info.metaData != null) { - final int preloadedFontsResource = info.metaData.getInt( - ApplicationInfo.METADATA_PRELOADED_FONTS, 0); - if (preloadedFontsResource != 0) { - data.info.getResources().preloadFonts(preloadedFontsResource); + if (!Process.isIsolated()) { + try { + final ApplicationInfo info = + getPackageManager().getApplicationInfo( + data.appInfo.packageName, + PackageManager.GET_META_DATA /*flags*/, + UserHandle.myUserId()); + if (info.metaData != null) { + final int preloadedFontsResource = info.metaData.getInt( + ApplicationInfo.METADATA_PRELOADED_FONTS, 0); + if (preloadedFontsResource != 0) { + data.info.getResources().preloadFonts(preloadedFontsResource); + } } + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); } - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); } } -- GitLab From 1fc6260065697e66d755ca0ea9b2bbca62d94314 Mon Sep 17 00:00:00 2001 From: Fyodor Kupolov Date: Thu, 1 Feb 2018 15:48:53 -0800 Subject: [PATCH 021/671] Send SHUTDOWN bc to registered receivers only Test: adb shell svc power reboot Bug: 65174075 Change-Id: I652c7564b989736c9deabd0989abdb1e44f3b78a --- core/java/android/content/Intent.java | 3 +++ .../core/java/com/android/server/power/ShutdownThread.java | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index b3c8737a283..40712f1865f 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -2508,6 +2508,9 @@ public class Intent implements Parcelable, Cloneable { * off, not sleeping). Once the broadcast is complete, the final shutdown * will proceed and all unsaved data lost. Apps will not normally need * to handle this, since the foreground activity will be paused as well. + *

As of {@link Build.VERSION_CODES#P} this broadcast is only sent to receivers registered + * through {@link Context#registerReceiver(BroadcastReceiver, IntentFilter) + * Context.registerReceiver}. * *

This is a protected intent that can only be sent * by the system. diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java index bd4aa1cce35..0a6b38fb2e9 100644 --- a/services/core/java/com/android/server/power/ShutdownThread.java +++ b/services/core/java/com/android/server/power/ShutdownThread.java @@ -451,7 +451,7 @@ public final class ShutdownThread extends Thread { // First send the high-level shut down broadcast. mActionDone = false; Intent intent = new Intent(Intent.ACTION_SHUTDOWN); - intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); + intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND | Intent.FLAG_RECEIVER_REGISTERED_ONLY); mContext.sendOrderedBroadcastAsUser(intent, UserHandle.ALL, null, br, mHandler, 0, null, null); -- GitLab From 11c2cd5249db192dd0330697abe3ffebbed92ad0 Mon Sep 17 00:00:00 2001 From: "Philip P. Moltmann" Date: Thu, 1 Feb 2018 16:11:22 -0800 Subject: [PATCH 022/671] Isolate print out of process test from other tests Test: m -j PrintSpoolerOutOfProcessTests Change-Id: I9a984a2968bbf3e1ab0de6b906fc9ee52ee2fc67 --- packages/PrintSpooler/tests/outofprocess/AndroidTest.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/PrintSpooler/tests/outofprocess/AndroidTest.xml b/packages/PrintSpooler/tests/outofprocess/AndroidTest.xml index 72be35ba359..15dd64beb4c 100644 --- a/packages/PrintSpooler/tests/outofprocess/AndroidTest.xml +++ b/packages/PrintSpooler/tests/outofprocess/AndroidTest.xml @@ -20,6 +20,8 @@

* Another way to enable write-ahead logging is to call {@link #enableWriteAheadLogging} -- GitLab From 57c8b961dbafc99a448928c03f71e7279b30b74a Mon Sep 17 00:00:00 2001 From: Florina Muntenescu Date: Wed, 31 Jan 2018 22:44:49 +0000 Subject: [PATCH 033/671] Allowing Typeface as a param in TypefaceSpan. Test: TypefaceSpanTest.java Bug: 26946279 Change-Id: I99a46fbc41ada567731f034f515998654ce77cb2 --- api/current.txt | 2 + .../java/android/text/style/TypefaceSpan.java | 109 ++++++++++++++---- .../images/text/style/typefacespan.png | Bin 7749 -> 8753 bytes 3 files changed, 86 insertions(+), 25 deletions(-) diff --git a/api/current.txt b/api/current.txt index 10fdea4da89..f23f4061f10 100644 --- a/api/current.txt +++ b/api/current.txt @@ -44417,10 +44417,12 @@ package android.text.style { public class TypefaceSpan extends android.text.style.MetricAffectingSpan implements android.text.ParcelableSpan { ctor public TypefaceSpan(java.lang.String); + ctor public TypefaceSpan(android.graphics.Typeface); ctor public TypefaceSpan(android.os.Parcel); method public int describeContents(); method public java.lang.String getFamily(); method public int getSpanTypeId(); + method public android.graphics.Typeface getTypeface(); method public void updateDrawState(android.text.TextPaint); method public void updateMeasureState(android.text.TextPaint); method public void writeToParcel(android.os.Parcel, int); diff --git a/core/java/android/text/style/TypefaceSpan.java b/core/java/android/text/style/TypefaceSpan.java index 16228125020..908de2988be 100644 --- a/core/java/android/text/style/TypefaceSpan.java +++ b/core/java/android/text/style/TypefaceSpan.java @@ -17,6 +17,8 @@ package android.text.style; import android.annotation.NonNull; +import android.annotation.Nullable; +import android.graphics.LeakyTypefaceStorage; import android.graphics.Paint; import android.graphics.Typeface; import android.os.Parcel; @@ -25,33 +27,69 @@ import android.text.TextPaint; import android.text.TextUtils; /** - * Changes the typeface family of the text to which the span is attached. Examples of typeface - * family include "monospace", "serif", and "sans-serif". + * Span that updates the typeface of the text it's attached to. The TypefaceSpan can + * be constructed either based on a font family or based on a Typeface. When + * {@link #TypefaceSpan(String)} is used, the previous style of the TextView is kept. + * When {@link #TypefaceSpan(Typeface)} is used, the Typeface style replaces the + * TextView's style. *

- * For example, change the typeface of a text to "monospace" like this: + * For example, let's consider a TextView with + * android:textStyle="italic" and a typeface created based on a font from resources, + * with a bold style. When applying a TypefaceSpan based the typeface, the text will + * only keep the bold style, overriding the TextView's textStyle. When applying a + * TypefaceSpan based on a font family: "monospace", the resulted text will keep the + * italic style. *

- * SpannableString string = new SpannableString("Text with typeface span");
- * string.setSpan(new TypefaceSpan("monospace"), 10, 18, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * Typeface myTypeface = Typeface.create(ResourcesCompat.getFont(context, R.font.acme),
+ * Typeface.BOLD);
+ * SpannableString string = new SpannableString("Text with typeface span.");
+ * string.setSpan(new TypefaceSpan(myTypeface), 10, 18, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * string.setSpan(new TypefaceSpan("monospace"), 19, 22, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
  * 
* - *
Text with "monospace" typeface family.
+ *
Text with TypefaceSpans constructed based on a font from resource and + * from a font family.
*/ public class TypefaceSpan extends MetricAffectingSpan implements ParcelableSpan { + @Nullable private final String mFamily; + @Nullable + private final Typeface mTypeface; + /** - * Constructs a {@link TypefaceSpan} based on a font family. + * Constructs a {@link TypefaceSpan} based on the font family. The previous style of the + * TextPaint is kept. If the font family is null, the text paint is not modified. * - * @param family The font family for this typeface. Examples include - * "monospace", "serif", and "sans-serif". + * @param family The font family for this typeface. Examples include + * "monospace", "serif", and "sans-serif" */ - public TypefaceSpan(String family) { - mFamily = family; + public TypefaceSpan(@Nullable String family) { + this(family, null); } + /** + * Constructs a {@link TypefaceSpan} from a {@link Typeface}. The previous style of the + * TextPaint is overridden and the style of the typeface is used. + * + * @param typeface the typeface + */ + public TypefaceSpan(@NonNull Typeface typeface) { + this(null, typeface); + } + + /** + * Constructs a {@link TypefaceSpan} from a parcel. + */ public TypefaceSpan(@NonNull Parcel src) { mFamily = src.readString(); + mTypeface = LeakyTypefaceStorage.readTypefaceFromParcel(src); + } + + private TypefaceSpan(@Nullable String family, @Nullable Typeface typeface) { + mFamily = family; + mTypeface = typeface; } @Override @@ -79,37 +117,59 @@ public class TypefaceSpan extends MetricAffectingSpan implements ParcelableSpan @Override public void writeToParcelInternal(@NonNull Parcel dest, int flags) { dest.writeString(mFamily); + LeakyTypefaceStorage.writeTypefaceToParcel(mTypeface, dest); } /** - * Returns the font family name. + * Returns the font family name set in the span. + * + * @return the font family name + * @see #TypefaceSpan(String) */ + @Nullable public String getFamily() { return mFamily; } + /** + * Returns the typeface set in the span. + * + * @return the typeface set + * @see #TypefaceSpan(Typeface) + */ + @Nullable + public Typeface getTypeface() { + return mTypeface; + } + @Override - public void updateDrawState(@NonNull TextPaint textPaint) { - apply(textPaint, mFamily); + public void updateDrawState(@NonNull TextPaint ds) { + updateTypeface(ds); } @Override - public void updateMeasureState(@NonNull TextPaint textPaint) { - apply(textPaint, mFamily); + public void updateMeasureState(@NonNull TextPaint paint) { + updateTypeface(paint); } - private static void apply(@NonNull Paint paint, String family) { - int oldStyle; + private void updateTypeface(@NonNull Paint paint) { + if (mTypeface != null) { + paint.setTypeface(mTypeface); + } else if (mFamily != null) { + applyFontFamily(paint, mFamily); + } + } + private void applyFontFamily(@NonNull Paint paint, @NonNull String family) { + int style; Typeface old = paint.getTypeface(); if (old == null) { - oldStyle = 0; + style = Typeface.NORMAL; } else { - oldStyle = old.getStyle(); + style = old.getStyle(); } - - Typeface tf = Typeface.create(family, oldStyle); - int fake = oldStyle & ~tf.getStyle(); + final Typeface styledTypeface = Typeface.create(family, style); + int fake = style & ~styledTypeface.getStyle(); if ((fake & Typeface.BOLD) != 0) { paint.setFakeBoldText(true); @@ -118,7 +178,6 @@ public class TypefaceSpan extends MetricAffectingSpan implements ParcelableSpan if ((fake & Typeface.ITALIC) != 0) { paint.setTextSkewX(-0.25f); } - - paint.setTypeface(tf); + paint.setTypeface(styledTypeface); } } diff --git a/docs/html/reference/images/text/style/typefacespan.png b/docs/html/reference/images/text/style/typefacespan.png index 67e2cf9b04684bc601618a3a9edec516d5d5d612..1651c390e71515f0b93e0d57c78de44980d5844f 100644 GIT binary patch delta 8689 zcmX?Vv(ZJdGr-TCmrII^fq{Y7)59f*fr0S{0|SE(2MYrOgEnuj@`d<{~E7iIX%=P0GpCt-iiH{rtWge>3dTzt5Tac59CJHqlJ4 zBnD4|CWTIiCpI%0PR`AW4=MF6^*^7`ro`Ft!v36M{qomg;dehgdA@V|;g=rVx?2kR z70Q=eUvT2qt*Ogsep|jvTzAWbL+O3oQBwO_#olRdkpNMO)S~LqO%oMtvA)|XX6L%) z?n}Nc1?5T{tGZSdeYzF6_i}93!(fN*htU~L3Fjo!d$mr!Hq7Y$`>A|awC;4*j#meJzT?;Oq{1U99zGB6VZ|Zud zqE{rk^2QtOY&kLAd4uQibSDv4eQDSI`5HCHQpEdBFGW7zKkw@@4*p&H-~FAu>>%6R z`77cJ_bgbFajkT3`N8_ahpzGw9>?TvzTuQjyHl{g=5+Iarv%ov{w>v~oSu0Fe3@k^ zQse8-Jn_Ad9djAav%L>@oyhaAKQ>wL=KBT3zig%o>K?RQzvygjsWMMql*73V+x!pk z9bEGHSgdi@rJnp-mF+XNW|UiseyH^SwTnY_;n8>T0&7-0w6=}>ZaJewq@Jh3E1tQ! zK1}W9j!4Uiz4IrsPLw@3;eU>kWBs$Nle~@>Qfm+Y6+V?={-g7rn9S$=Q>&6<@Bf_H zms`4H>J6O<*Y$&zPObmX^w2EuSJlpW#fN#6UO%y4ey7-AnMT6mZ|5D)i-@ea{Ma>l z!SR5*9jDzwS!2!W4$aiiTspDykGv#%eS~(_Iq$z)EvG(t?-4QWqI1=G^~%#xx2_va z+iIKnHTlPq{2kUCj~$v_=<1mAxGOIE&FnCjPrl88im@-AMy*h*XP{ocfK!s zyV`N>pY3zb1jo(pOVwCc7u(&X)npc{ZMEp5jL6gwm!>b~mF+r@S&j8}q&Y<%T=3RyODbD@=}D2MiEMKv@6;E_KFKxB6q?vSd8OR>Pon>#iFauDK2HN zZ-QdyMgErormyzQPAiqzayaehZJqKf35r`^hK2gh*q(B2h2dkqo%+JQJ07fv+q>&) z9yW5)=jIl{Vd*3^EkNSZdv-HKVRR@HM^PJzs}Ui!{GIv&+BFdg-pG^ z^1fPh?&V&`s-?QKK6U=Sf%q|4R?o4{^nH)ch*GYoUOXL z``FaUg5RqzT@T;V*cE5?(96rr@#N`yE-6m;)_4|oo%&Rkwy*q<_KEDt*PmCr%{5Cq z)wHRUZ}&6)d**Ak*55xo@6!DX-IwD^jxlwbe%W{3_I2Xhb5+(Gmpzi3D*JJvN6L}O zw|Xq!)z^lfQ4Mo{GR6Guml`*fqxa{0TxH&)&ZJUwNP64vRm?@cxBV?&O~_xbcwJtk z_JOH*u+{2b^%q-C&c8W%r`_}KdZ(o~U47lRW8I{C^|yBKdwWrB(Yra1{dVqr!u#I( zQKo;EbHP#j8NxO3)5DEICR~2*dH+Yt&)MhsvJ-wR$cS;cUT@?re5T@H_A}$OpsP!! z_x=n#_30qvdg-Hu{>xOg9WTD0Bi5Pob&c6|U5+Jff0$P7nzpv_bawcWl2`4=&dYx8 zvpm@IaaY}RS!2x?B?0ZLPRxjO+$~pN%$ac2=k}yY8o8-Hf^VPw**kgVUSSutJqu1> z51Vn4HGfCk^iMjK+CQ^H>f;}sXFq6J+?mJoY_6-{l9YoJ826VcCN5g=D%rl+aY~KU zckASTN{2s9EdO(8MvPV(^SYlq_Z*+|ebSq{mtEf{&#XV>czn;pU7IGHbI<9XWAr{h z=aj+G9rfSmc}dOf=>0Ctc2|FU&hOb`e^Q0B7ykTn_UGE`XH-?@_#Y{H!*(v(qyD7!ZGxqukk4> z-=Ceql3EX@iHq87?fO_VFJf}r{BrIu*L`0+QaSspJbwk(uH3Dj=T>kp`>;^)zIU9M zBQv+l{9&wbi6m$OYwzV_Si zRK)Q=tE0W^Pfagc_V*lLuvEYMirV#0ZnNPF&bK<3 z@2OvW3CR+3zfmpB=eeCVYzV z!w%WfE|p7(b3G0&&~^LrQeftnM&ngQX)@d6^_DiRy6&41?ZjH|E*sa#^^WD*l85=T zoKqHPUY@%5$qKD)mFH@A1r!}TAA87}@Al*kN#~UBnJ#-(+y7dW)9%|MB`cfk`KuW1 z-OjzP>wDn6Z`zWMY2C8hBiXxV{`#MA$y$r)RNAX||1Vfr*({0<4KPw%aNTO+gTnr# zYyZu973-(InBNy-cX$y~qhouh5hsdK;5&4mNaGj=vybD3<*75M4?+A3M!tuNMm z?mQ_o$9!_z{HnY6CurErd@)~VRbSNsz0iZI`%AQiFGTa4dK&Y2;m0Rg$@4$mn&bOy zgQZ`E)kfu0{~qjJrd)UATidseU2#)nKmVBYs&DBD%a^922NTcVo*-F#a*OdY*>&~1 z_rF-QYEIYdZNK_#Gwq%zI>&{G&MCh9tZl|AyWDTLSE(y=$$j$g<9zg?RI#wAc~fWV ziU)r-g%qT3SeA9QaH)#pHj^B&nm3`MIbW37MIGAr?pxcCDZg}<`KFVK8`(>u1bTi? zEp$9L-Bz)Ej$W+QS(TmdzFL*Oj0$urJ>5OMwq8(v*;19zV9CS9r+#bQ$cZm1J*QiE z$}sLhSuEF*&J*{Ruy4Kbx8l)d-rpOS^JY!I{vsLt8W_#(aUG>+rd*zpZXsy`rW=1B@ zk7Xz50OS57OBiB7h4rj6C zSKXy4;rp#V|HKys316F|yu7LQrXMf7|1Ec7LfN@6*)FBYr?ZZpkozC|p-=hc)U)%v zh3uSnz5U?0>aqT8gPxUFYL?fZWn`ULm+Lk2x!b2APp*|^Z+k5F`u#BY`!2lJwLkLz zql#4@pVtH%yq)i(e%-3(xY)!wf}ib#G;gG|JKp)!nEmqBbm7y<=d_mv8(vx*e{%_I z?z}A<*V%45TK;yz=Y{iUdAGh;@^*XV(}Z)XjDLN5o~Ci`e62R=qs`=nJ}X{ryck;V zXd3xc=hH0Ci%olker;Lb#g(@uzBCQyqX|S0jYI*yUf|8U%h@gMYuk4mh<^@CmS3sr(d#GvMp0zmZoCy zCPb);XIab!&gnkO%K6m_+Z|;+BZUsSRp(rK*k?q|^VaufURw8Ljr)^1-jTCy+gzIL?shT@OMZyY@%<9C zJbvlkD1)UhY7S*F?^||x&!#LXsTRJCcBhwlCM(OC{aF8N@}v7*TxJpB&#eMKB`Sp| z?VaZ@l-yi4^SXAB-|fm1a`mdJqLWLv)}GbYd3ABw@lwm+e;23QY)SGxv55WtJIA}; z@47FkL>2#9byfb#yLG$P_2?n+ndyK`II{#91LE|t@6s!)R_Y6`+G+01%D8<})6GUv&cedFq$5?EzkOlk-!bc6>A{{o#hKaD znK>J`zVuc8{`RGG^Wi>~N0uETvHpi^E06Czttha(A@7L1pG9?!} z&ZQ#^a#hPB&Us?j(cT3dCLCTQ?c^C ziM~b3jWzXokGs@*6LdbN%b$4C@7;Ou&U8;pz4t*^?#x~)${Cuhbz0z{*9p@IS9V$7 zXKE6~o{GHxUEGBH&9f>_C;3;cIn8pwOHWqn4 z%)H_iRb_R+ck;{oH*NgZo=e)e_to4K)7PdM_gBemf8f1rZ+)7d-;-Id?`^oP{LMtO zXl3PxiEq8VCM=k-CVyd`?Bl4e%YM6FO$!Rz72R#e8algVHEWu^uhx$@AYZR+N!W4n zW!mG;ossJ%@b&EWcJ%VeHUUj0??=%VV&yIqX09O{|tD>Fas^QIf$-z;Axxck!O zHnBNj9!}9u?#x$PziQ_4E7!}*S6)5*^n^jZnPAv%p_kr1SE}0L&t!`1eZ;TEdRo?5 zC`iiTZY7gx%57=g&WM`a$k5oRCAUA9RhX6*E!*=}ng55|aIR#E1*-Ex~d@4Sz_ z#=ZL1l?Ups_ew24{@gW7uT=A7(Ym}_Nx~j{X*0IvJT|TK;_-K9FLkx7@xAc6tp0>h!oEgcYuQhZT6Xpq<{1_2|GQGz=w;5c z%t+a-Kem+JefHYs;!&mNJKEM94$p}X@{IR&&Pdh&x%SbdT&1rw&i5?8q7pJSw_hjg z-wu9D<#|!dR%E^O+Fp4fPkqnpIg(G#RNof0&si~}+xEwT2^?G8V!YDQqN7gA`}~}0 zF3KufZ+0s!qF+zes^rJs>#VV6%{Tp9x$NTCOnT_7=C*&IX7;h&Q};bxnCkn!=I*zS z2QA{0UiR&M`J-<}3(56%q24 zbNSIj^Wyf-d{I7|cbDjw7r_@+diUEkpET=tRg3T0$s+&TdDUF&oNK8Er*!TTdS)RP zdw5EbTfwoNzjJI$3B+a98H3g4@7c`xhUVZI@sr>fkFAMoi zlXN4tH?5zW`h9g>qSFPn(=EYtvU8bHGig;cl!QEk(LuR z?LC_d42xE-;K`femGsN;rgxjol+5*3FRVX(x;5#9)6HH}c?O}*lAEvX`YSK8uWCB2 zmoAjjEmxB-%ynw#!={SO-;=)m-(eoQ@os(mEj6#tA|L-=XPxM~%k|K5C)WAqVRMal zOg*(G&8_;)=Fl@8VL!H8C|<66x=7)9ww%zz#VOJrWw(w^I&$&V^0gDL+?rd?bXU&1 z`u1{OV?FCuKK|>u*JC`KlD;0Cee2x;x9N+LLR_zuTu<;}`N;NYYT!!kZyKFXULBfN z`Fom-tYrPo37ux`Y;Egvb2gYO-|KU2uByHq)Gh`gK-`ZXPy2{JXjA^4Hv2 zvi|3ev%)FPR%g0nf?__Mn4t1h%zpZw7dsS+_NT{u-4|(mZ_DC`yC3^@$Avj<-?K^5 z-M90Nul8ysW~(!^zs>WUsLQK)VAkJBzLsgu#gWsK0&Uia%-vhNXJ=IS=lY|Zxo*Cd zT86pG(K&^i96!Hb?HyYmYVA7xC!IG4{cW?1p&b{}wi?=K@-0bCy>%Clc zkJ)z_>~DGa`gdg6MMlf*GG|2=A6t~|P`zvCwnwR!>{Ir><}H5q=geWN$xjjrj&{$x zs6C_nV76Vzm+0oqnV)Y3TuonWY_jBfmgSGM6FXnT3Yt|JEIpsyS#Ucf{eS&Y?GoLS zx<};xEO!OPeD0;=$8yiLsj-{6x$*4KvdF$NZP&Ls*>*)1n_CyW;&l(%c(S-Eck^k( zgY9~^V!z*=d1OIc?gQmZ%g;7tE_dOQyE@fwz58~}$9*faJ_j6jJJYEmZylk2E-UiZ zK2`SgD@U9czWbH5WO1=*_3mOxS;?6Cuk{-jJUFP5B=d6H{o^yAyKwP7KOSautM1^(2xMDss$=U$sxdh+a&51)N!+o*9yypogZD6CmgF+VXM_;-yf#6LVxY9 z;0V>j$DeQ46<^%{W#|3SgDuMY=FePnB65GhF-?1qX-XC^S-D?)%Z&~#t-Rr>dima# zy?vaIl4mPzwY{-@{u;ZvX)dRfcE9q{z4fJIif?i=Gp~1jiE(jW+_ZyQC;u0hzMmhX zX0_XcWp(GdXS~;ro=Z+!IqT#N=ihs#h+R2m{qmKt|JG)iN7te=4SjNVUy$=Z(fMYZ z-PIEZLpCMn7RD8Kvd>NZF{|zU20h((oX_@5{h9VQ_qlIW`g-k|*;k*$EdIeeyXNX| zy=8Ah#U~}LJz9`*yz5E5M2vZQu~+QYtfqv6*?Ni=A>x}>hh8i`eDjRlu7q3pd*`dP z%s+AFdyLYtM=W2G_8wYsze>IG^&&_gTeeTJPM&WJsCs)7P z|2Ado(o5S;Cv(kA*PRu9v+0&p()`tD9p-)(l-H4r`Mxo?>m=j!j~}lv9`?R7`=!aH z!rz^|+tN3^HvJoVu43Ut-m)t7$ByAKnLNd@ttCr#g1gnOn;KmK}=!bWy^mg2gVIor#o#1tKI*HVhEQCC*V@OC*N z`Tq8L-z7=6INf9Y>Wr+vJy@N+r0IloU9R7LdCLO#t$(ykYYeAGp4~g?L|Xj!{4TG! z(mNCUTY&VG;X z;p0oq9k2Ga_2lecxMX>=@L4!-_ zb$FnY(9Oj9Mdn%IHzL$~tlpOCT+Lwit~*$|b8@qC&bQ_-^&eKw(cd}YUGM7so6idG zn<|iN#v!5FZq3U7^T2U$uhea`e72o;@v{4}VC}v~*H_B#uSjoCw^@?4IQ`G&n`(Pk zNPTz;a5c}Dp?-S<}O7R2}SiTKX+$yxk%U;Os(dtNlHN;pxa9`lv|+G|mt<*c`( z{}|l-5q(&Vb#s%Zz3HO(`m1YZtA*x#ZkT>{spXHY0geW*eS?egs?P7Tu6pxb%kM;~w&|4VQ)m^mwmMcU?V;aerw{|_xznN_dz+hq5kW3|P*LhB%=`_Jr-2O2I* zdY`MVW%t`u=;enQ^V&i@5yc>V7+&qm}LK{dXJqbvV*|UVF}bR8>^J;NgWy zay}YevsAKXecyK4{BDuk+9k_QGoHVatFUUv%vX0#)Ov3^`Bkd!;zX?*?{D_}lz;iz zR#g4X*(iS1bFn>~$HV!*=F*E3Kca%_!FO z+*NyePqEk~m2`p3?f17!-`JbAyM50xExT8nuO0Slw@E+cz3P_`e_d{fz|w;kzNtNW z_xXsH>7SlFS*7Udz7viX)f{=Pr?!0d^DWW^``vTYJonZvk(Zsf`gi5i6ARiWPyXO{ zYucpHgWGj?)UVLVFy7L2zdX}Chaq};&XL3mRjRGKnr=y?9$c_v%jP&?f3)~|!Ynt!b|&$-ZMQh!X~=EM9wZ>M;Q z%Cg;kzoAsiPt)Q}!`E9OuDfjVPe{8>buHTU_ET#0CG#h>H>>om=U=(|wDjha4ea^J zXO1hL$+vp8>gJMdw(BmvzL3A%xy&@+*&=rPly>>wbAvr?Gvbz?l`C7}xzK<4`xhU# zMW5VuZO^(d_kw?w?$H1E;JOp*#QG+8pD2Sz6K~H;|8rAmPT!hG=4ZP)Z(b>#{i$Pu z%q*MPPt@Io_DUt|d472EMaS@5_sfYbcT3|QO!=(Fy0>gilD;VSv&f+B{HdFjEcbe+ zrJ+a^C=zc_{2bKgUQCr1U@?ymN$Khmy0g*DdO&*pjSmCt?W z>tCVn9(_FzPNly@6MDU*WGo|JSHJC{53USco;wVD(&@ba#mwm zY|`4&qYe91zx(b;aB`hKQSIx;gLAak&2~Rnut_l0)JH&PM$zRdi@0u;NNtJp7Y?29 zx!T_OgLAy_dxJ|dEOqC$WAbGY|K43acPZ*sq;{*TVzxO?%{F<;H{`A@GqcglE*?A%>5Gt9VeRz|GcTd5y5Z?)UE ztbczu?f*V)!SVkd^^^8)e|@#6cuC@2dri?hKeyar%|3B>{kFy0zR%wFYwac?9x5}z#zxlq3|5j+2Vf01yISZeyou6~|pTws4wKwK_Kg(0t z+7PGz%omH|sh_T7Frr*ZS8N3dNsPyjPpj z^J>cF(D3s&nX)zZWIjBnpqH|0-GyJCoBzkhgxob~H&fQ_opx{*SANtEe796y0bR_eJAek0#Q69Qnxz1-$i9CY}L1eELBkaQy(HA ze)MzyUM&U&2GtVRh?11Vl2ohYqSVBaR0bmhLknF4aYJ2W!w@4QD??K&LlbQS11kfA g@~EQR$sBSriWo9%?58d=FfcH9y85}Sb4q9e04Wwta{vGU delta 7700 zcmdn!a@0n#Gr-TCmrII^fq{Y7)59f*fq_Ymfq@}_gN1>C;Sc+kFB26b*)B8eS~#;| zVuM1xw4e~*`i|Nu3=9mMWd)Au3=B*P3=GUi7&Hu7Ss55)`8-`5Ln>~)y&G8^bA0N9 zkMZ}k*06l9jaSVRay_|8Cu{qYJpH@hO8W17`}T4S^&wZ!&Q{NVcJfTI-#o7Q?vj#{ zlG}YhJzwYG=5}nar`)!GJF_QFoLJ~z=(p|Di4!Lx6lQ`c0V+}T*i8FrU(RZ@?^VmH z{hPOx#;Y1VEo?u-*P9bzBE(|6OvIJNa<0{;X}?ZQ(Ck-hsVx7{R46E0m6djbr-@U2 zZo$dYqox7+jL-?{~#&&Sq( z-QAUNV7;t)=;y5ciR~qIsa2=zbE7o+AI#bOIZXbd)$aX5#`y)8<>ziZBy4tnQ#U)S z@-f%sy!5?BnD#nA6{4PPV$o58k~1g z>u2gMQM(V57G+%OFAaV7qcv>4#^&jJLsagzJx$zSw;{w6Ne%;f2Q&Vt=t*&XX#N zpFJh#*!=m^Zckfr<3rSH$C#Ka?*fbxge+f+*KUt7TA{G~$t=m(sn_#&>2*zu+edlY@wpWIV>CsnrDPMe_&gIqjhj-A}oDmNTU zi85HXAf+nsy&TUjy`Nk>8x|FA@VRfhj>9=~r3nJ5B$a&vJgg?(m(W^Rb^Lp9gsd@13nXf3 zEh;9m{_LCoPj^lI?|tdI#w?+zI}3B>&nyVOC}g`!^>ft%M*-j2h7a~MG(8Ln%Dg+R zKIB!b(Xy5lzJ4E*S#@dOc4x$p654~urbH8hncW8;{WM37{s%yrjhtm($S00~RcDKYTlEpIi zTIGqvy}Hl886KA@z3Gy=LG4r3nvM6(rS=qko}Ro;-E{GNL-C$vUU@5Z`EEomuIc`_ zB7Vc6au)TPCzA?QJME%nHwEu|`FEYgviqfz7rFFbeYgGms$FJsA0}yDOpG_)w&342 zzguf;7EO*Ty5_U=G>`RrXa3*yX-7}oN^B1A;W~FCqu%%M{)f4NTW+$c{ypb$M$eqv z&iTsIf^N%|mlxENCXF2rg*+x!MMt15&xf46BJIo_Ud8WrcW?&sW?!@B(M?(UW0t8N?F3K{1~nXf%ucenK1 z!`l-VTORC~|1-j6k#~E>tu04d>?Obd)e?7_;L-DYXR%k2l}gvLi924Vs-C~jvs-fg zkG+~KvAa~$d^Ebw{VK0NDz#NkrD=}$zpFD@zit0^(e0_=oG;quES1$Z8FS|d&ab|| z;zg~r?aVr(=Y`9^a{E=D4}N)7_+Ns{0cXCBU3(H@4Bm=Pzxb9_+oiKtcb57_-|3ph zdKD{H#Xa`xY)ZIcnZ0VAoS{aCnYqP$#al1k7d_Zwuc>wOx!{I1dAmzpJ?rn~{a!ms zqV$kquu${8ZJFn8+@7?)(m*%$h`rQ7HTyLid0SRFimxr(?|87GE~<}=o-I{Cb*e$sc@bwS^rZd(6)X}8ctA%3fb=9;L@r_V{O z3U1o2?Y?^XmtDyTt?q9&_*^VKUXvpwANBmFm8$v6HFIS7q84A?@!77#H{iprvvMnf zOx^b;amC7&9H^3YoBMH_w~%t&wx#P>Is7#BHtgRzd7EZ`!j1Plo=Qh^{1?}rehQ0jcDX2>P`YZ$tL%VFZzKDoC;j{1 zu(fWv#tZYTr*n!O!?PZ7dkMKzdcU4_>1zFR*)RJFFWc1@ZFp!AchOV0iSM^`O4p+H zzXv95`6v4=`qlrPI)$f}o37HRa^KI=|6|?0FI@{xKMwoVDH#0k$v&-{yR?>_x;Dwg z>c{mT8&ju+RXSMRS$^Gqr`C>#PQ9C!Kk-dhSu}Ou>x8LC7oE3a_r1PWDKEY+t@ih< z#aG zR~mNT3A6rl#I;k~E?Z;fBiS#YWNQAW{hXHzyUqG=&AY4B`7D>@ys?aY>a^$lVxfZ# z*Ux&yeBC7(I6Ymz~FNHQy6k zcKoK4$+d<5E?4?jy5%ozo4apb-W)gHJ4RM2VRcgRnI+xs(K+I*=`pLS8xF3Re5$!9 zee%LxSA?g9ypo@q^KFLAGTGHujM6-n@t#}m75`ebu4B<}8<$%fvt_4mpR{D+oU_xq zGV5oqy?U(Vywq`at%|;=^T?Jv$>RZ6^C^ za(=6Sd3fuoWInaI;;z|icPhVJ+`BI@*|onaSA><-{PyJ|p{d_19NHf&OD`$)ayB#Lik=U6Oo%pG~iq>PEk~n;cQA zdHf`Wro1Ux@lqi(=D~L7zLj5Y?Q2_JbHePlRA14BhxHD3Uu{g;{(EZk_urSFi|KxE zdjF$ux%8c5voD9ct-8a%`TJj^-_hB(w&ZfI+o9B9cI%2kaNdSvkq1L3PjyY*a%l59 zDdTshkL?6D8s6QcGbLlS-C?eCl`263Ka(O#1*NwXN<3|=RZ4S7+)sz74M*a3s!4D3_2s*xyZPLL2g_DZWi3+tI-~UF zwJmeLR{3zT`It&Liu8CE_u>MT_wmodi*=L1UL+Z0Oo-hxw z-*`y!;0)<$p{56G<-V?ZG3E5J3r}ZSe+yZ^Nw3t~?}fMcck_+c_@>VdDLPfLt7gxP zd!^CQ=gwZ))$e?3%SK++>%zL*@2m3s+HX3yCh7jc`_^>WwCL-n>MyqEcx@oKH#Yj$q+m2W2mbGO{8?DYP3 z_i@<^-{*feuaITYM{f zv^-RP{q+_u)L%ML$~@cUob{tE#$7L)eg>>MD(-hBw|%mNQNqfMsQpa|%T>!>80H-i z-RFU#^f4)^{obN@AY+0Ap4uFQ=xiL6|k z5hLL9W4~|tHr2;XH&Vi4I&^~9?Rv0EQFtYj<}9yd&1~^EPpdB4`Y%qXe$p|`@{#y4 zRlDMIQqj4aE@tiBZu$00R2s+RV_M6uY?9jXZc8y+kLHs4t~D~E#{a+VO+S3O``D%Z z=d7~_DW?04{fdP} z=6sp9`Pi#Xhg!s%_r|QRKUZ0H=iA9biS@g>G{xumFODp~oqai1rPQya^VCYc{T&A- zKlXi(F<$)b%Bj0`D>nF^T3pgSH|*y_k=Yq9OQ%T$mvyhpaQ0o)8@6-Bi#eHd&q*H3 znmzT)ji*y41*?7C@o?6KOTN|HFTZ3w?ju=#=A6mDud9lg%C5JX-k+r}vvl*?`iQ)V zOU_jX-oI8p?VII{PhIxQCcDS2SFI{}bs_P}%4O$1&$yOd_dR#MOlAMAcPo6i0 z7Px$G=6>}kH#t+UnEg9B*!~<^?zL!6ocGry2fwENK6lsWHqWxnjwjilXPk&RT+N!l z%X{5D zPod=T=YedeJ@e>h>OHuA9KtQ~O}C*_G5*-bIrx*V~!) zuASPj@`zvTjt5g-H|0gYui6}wDSvvE?&4RkP>fq>W0U; zZzal~IuBNs)ZhDd!tiN%-K*twmnQ@%L`?s-wzI9+<)8q2Y zHh=yrwTaj6y5F91+=!j^OvTxwvyz&RME-wreX7Een{POhoAxN1Y219Q=vZ)aHE;Cv zqRgdj26w}z+}+wEz3kb9qATgk6OKt5-!NJ(xpvnPBjfH@(>33}sBfOQs-t^nbZvL; zq8C#(v$4+ocK+kp(t{Hgo6huT@_au_K;>J=R@akayIl^luAiCrLhb37rgE{ThO-}* zn7zL#p6c#CH~W_9Vp%gy&UF=6T_#sujq<*w?A{;hwdl27_dSGTXLr@V>7RjuewaC7z z{<_cA%|93SUvB-vY#O`uv~XC5*S{yz^{!u8o^nWaedSNS%_rA;iQeT5SAFT=cc$XL zw{F_q73Y&PGrn2Zy-V`l%jH({B4qYcjqOEo25(j6XRg}yuVwp`DZwUJ`M)IBz0{g| zUheyBll`~iW(Sqt%$&`lnqPf?iVI)zz0Z@cyp6S3eEHXx`#z;Mht1sfTPo-yLzgetX@*p(1>}8ly zX3WWlZFlBxHocu(zqb2W?JAWW`!nMvMHcPq`L*->+igDQHu%>jMNRlzDxI~ir|j1< zao-u=b_ti=SYUnjVddEd$Hm*KKW$4=c>Q|c)v9zAz27U3=UhuXBzn9y>5SB4-D~mu z7SDNAUGraWzwBl^qwsQQc+BTDcSFBT`gY>u0^vT+bwARr-)z$1HTrX-(8cWSm)%QluHF~lX~z5R^D@DMy!8Sq z)9xBY2c0v^*X)?KY}pO{tescNrn6qG|5e$x)b;dRLk$hfhbuvWl+3@xGm&dq(At}c{qjpI zl~09DtW#x`u3en9T;glRsUTO^_m)vx|KEEhS^LuYU%k@%!*@#WF7kK0`0Er~+O$2f zpSqHQ&2!i6n3}!5@>1>8KCx^&+u7?KFHg!lK37urd+c{lmy1gbAFJhWRQ8wg2!7xi zoiF$@V$0e4x3toaxy)L}gx%-}ZXX#&gj_b?}FZBxvliyp%d_URz@7T7GX*W{@SKGYLUlSf;H0{|H z{p*Y8{(b8=^ZO~~SD%*5elgG3C}}zGxfkUpXEe>p%YM6M`J~5&Gd5m|Z>X0o<-E4& z?Y~kwtRbkGh}RwNl|?Ztw56TSfR^1#H>9^4E*9BX;wxQ!ngb%Jk0N zcJ|t`i7UA$F8($zyXO93yYho8G98zvn%&MV4whVO7r*`d@^g}*Q?}pn`#n3Q&r@d0 z$>qN;r*EpiV)i>%$R<;JQRr09)p!5CJ;yQMecQRuhdp=s%&xZ6)IYyW#Km!u_rAQX z2HAr7%T&|vuioz+V4%r0)%gPV`nPYw1xcr2j;!mIaoqJeGO*_7R?}fWcQ}%A#>ZO*w z@6m$bcVgNHetw<$NxggO*HFLZdYe}=O}32I=XkkBZgteEuVyKUMOUty&hqm1xHK=K z{=p;ldy`*$`7Y;iJ*0KZeY4+c)AV1N&9(c?7yI(r(!x19ZuQX;&+jZdn_Rd1&ZomK z*8SRFBK>sBy^YT=xjwF$z3gjj{j*nk8=r2|3F17|UmkMl&Xmb-N<;tJe^=N$=a+cd z`Oh<|e!eUT|F`>UaIfj4^WRtfjCS#IlFE;a{k}swg5|!eY&QE@G~qhKE}NqE z$_Qhlyy>pDx25Qa8G98qEeg!9a6dB7E9iMokAAmL<(*^|yMmDQM_L%KPu*~<_~x$1 zdXM5PgX)*3oO&#$yyIbn)bxO|TWi0(J#(*6{9=9XN|)pHjSIhCo&G`FZOY||=W;sp zqHS}(>#o;YI+1f`S7c7nzLy+LProOm-MOtCEcadFBJ(dF4US!FH&@C@Pn&dipYDmP zvUR63>=zcry;^<$(st3rS%u~Tmk#G#64w2E+I`ZG^_#`dN7Uq5A2NBIm!Qb?;B>#; znIEqvAFWJDNvYTA@Bz_Orfi-*-U^<|bcW1Y3jCK>uMyPdU!r5dz`&qd;u=wsl30>z zm7GwNnpl#`U}Ruup=)5MYh)f`Xl`X_VP$BdZD3$!U|@dt>?RZ)x%nxXX_dG&xHXjW RF)%PNc)I$ztaD0e0ss%>UFQG* -- GitLab From ab40df9e6979c955f89a06a358385ecec361c9d9 Mon Sep 17 00:00:00 2001 From: Seigo Nonaka Date: Wed, 31 Jan 2018 19:29:40 -0800 Subject: [PATCH 034/671] Use new minikin::Font constructor Bug: 37567215 Test: minikin_tests Test: hwui_unit_tests Test: atest CtsTextTestCases \ CtsWidgetTestCases:EditTextTest \ CtsWidgetTestCases:TextViewFadingEdgeTest \ FrameworksCoreTests:TextViewFallbackLineSpacingTest \ FrameworksCoreTests:TextViewTest FrameworksCoreTests:TypefaceTest \ CtsGraphicsTestCases:TypefaceTest CtsWidgetTestCases:TextViewTest Test: bit FrameworksCoreTests:android.text. Change-Id: I4d7af881ed1a6cceb79e827d88e050789da46d01 --- core/jni/android/graphics/FontFamily.cpp | 28 +++++++----------------- core/jni/android/graphics/Paint.cpp | 2 +- libs/hwui/hwui/Typeface.cpp | 6 ++--- libs/hwui/tests/unit/TypefaceTests.cpp | 2 +- 4 files changed, 13 insertions(+), 25 deletions(-) diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp index 937b3ffb9d6..48aef4a8b32 100644 --- a/core/jni/android/graphics/FontFamily.cpp +++ b/core/jni/android/graphics/FontFamily.cpp @@ -90,7 +90,7 @@ static void FontFamily_unref(jlong familyPtr) { } static bool addSkTypeface(NativeFamilyBuilder* builder, sk_sp&& data, int ttcIndex, - jint givenWeight, jint givenItalic) { + jint weight, jint italic) { uirenderer::FatVector skiaAxes; for (const auto& axis : builder->axes) { skiaAxes.emplace_back(SkFontArguments::Axis{axis.axisTag, axis.value}); @@ -114,27 +114,15 @@ static bool addSkTypeface(NativeFamilyBuilder* builder, sk_sp&& data, in std::shared_ptr minikinFont = std::make_shared(std::move(face), fontPtr, fontSize, ttcIndex, builder->axes); + minikin::Font::Builder fontBuilder(minikinFont); - int weight = givenWeight; - bool italic = givenItalic == 1; - if (givenWeight == RESOLVE_BY_FONT_TABLE || givenItalic == RESOLVE_BY_FONT_TABLE) { - int os2Weight; - bool os2Italic; - if (!minikin::FontFamily::analyzeStyle(minikinFont, &os2Weight, &os2Italic)) { - ALOGE("analyzeStyle failed. Using default style"); - os2Weight = 400; - os2Italic = false; - } - if (givenWeight == RESOLVE_BY_FONT_TABLE) { - weight = os2Weight; - } - if (givenItalic == RESOLVE_BY_FONT_TABLE) { - italic = os2Italic; - } + if (weight != RESOLVE_BY_FONT_TABLE) { + fontBuilder.setWeight(weight); } - - builder->fonts.push_back(minikin::Font(minikinFont, - minikin::FontStyle(weight, static_cast(italic)))); + if (italic != RESOLVE_BY_FONT_TABLE) { + fontBuilder.setSlant(static_cast(italic != 0)); + } + builder->fonts.push_back(fontBuilder.build()); builder->axes.clear(); return true; } diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp index 115d0d5a608..482d028a67f 100644 --- a/core/jni/android/graphics/Paint.cpp +++ b/core/jni/android/graphics/Paint.cpp @@ -576,7 +576,7 @@ namespace PaintGlue { minikin::FakedFont baseFont = typeface->fFontCollection->baseFontFaked(typeface->fStyle); float saveSkewX = paint->getTextSkewX(); bool savefakeBold = paint->isFakeBoldText(); - MinikinFontSkia::populateSkPaint(paint, baseFont.font, baseFont.fakery); + MinikinFontSkia::populateSkPaint(paint, baseFont.font->typeface().get(), baseFont.fakery); SkScalar spacing = paint->getFontMetrics(metrics); // The populateSkPaint call may have changed fake bold / text skew // because we want to measure with those effects applied, so now diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp index 091b5267881..dca9ef5559a 100644 --- a/libs/hwui/hwui/Typeface.cpp +++ b/libs/hwui/hwui/Typeface.cpp @@ -132,8 +132,8 @@ Typeface* Typeface::createFromFamilies(std::vectorgetClosestMatch(defaultStyle).font; + const minikin::MinikinFont* mf = families.empty() ? nullptr + : families[0]->getClosestMatch(defaultStyle).font->typeface().get(); if (mf != nullptr) { SkTypeface* skTypeface = reinterpret_cast(mf)->GetSkTypeface(); const SkFontStyle& style = skTypeface->fontStyle(); @@ -183,7 +183,7 @@ void Typeface::setRobotoTypefaceForTest() { std::shared_ptr font = std::make_shared( std::move(typeface), data, st.st_size, 0, std::vector()); std::vector fonts; - fonts.push_back(minikin::Font(std::move(font), minikin::FontStyle())); + fonts.push_back(minikin::Font::Builder(font).build()); std::shared_ptr collection = std::make_shared( std::make_shared(std::move(fonts))); diff --git a/libs/hwui/tests/unit/TypefaceTests.cpp b/libs/hwui/tests/unit/TypefaceTests.cpp index 2232c25de34..e424a266bf7 100644 --- a/libs/hwui/tests/unit/TypefaceTests.cpp +++ b/libs/hwui/tests/unit/TypefaceTests.cpp @@ -57,7 +57,7 @@ std::shared_ptr buildFamily(const char* fileName) { std::shared_ptr font = std::make_shared( std::move(typeface), data, st.st_size, 0, std::vector()); std::vector fonts; - fonts.push_back(minikin::Font(std::move(font), minikin::FontStyle())); + fonts.push_back(minikin::Font::Builder(font).build()); return std::make_shared(std::move(fonts)); } -- GitLab From ec96d0d3fd436ed35628a5f15fd3a2ad27e286a2 Mon Sep 17 00:00:00 2001 From: Daichi Hirono Date: Thu, 1 Feb 2018 09:36:50 +0900 Subject: [PATCH 035/671] Stop accepting zero size drag shadow for P. Since P SurfaceControl does not accept zero size, thus we also stop creating zero size drag shadow image. Bug: 72416622 Test: Invoke startDragAndDrop with zero size shadow Change-Id: I28f9cac419348a2550ac4db7de9016df82fc6385 --- core/java/android/view/View.java | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index c3e9e738773..9429dde1168 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -17,6 +17,7 @@ package android.view; import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED; + import static java.lang.Math.max; import android.animation.AnimatorInflater; @@ -905,6 +906,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ private static boolean sThrowOnInvalidFloatProperties; + /** + * Prior to P, {@code #startDragAndDrop} accepts a builder which produces an empty drag shadow. + * Currently zero size SurfaceControl cannot be created thus we create a dummy 1x1 surface + * instead. + */ + private static boolean sAcceptZeroSizeDragShadow; + /** @hide */ @IntDef({NOT_FOCUSABLE, FOCUSABLE, FOCUSABLE_AUTO}) @Retention(RetentionPolicy.SOURCE) @@ -4839,6 +4847,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, sAlwaysAssignFocus = targetSdkVersion < Build.VERSION_CODES.P; + sAcceptZeroSizeDragShadow = targetSdkVersion < Build.VERSION_CODES.P; + sCompatibilityDone = true; } } @@ -23542,8 +23552,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * constructor variant is only useful when the {@link #onProvideShadowMetrics(Point, Point)} * and {@link #onDrawShadow(Canvas)} methods are also overridden in order * to supply the drag shadow's dimensions and appearance without - * reference to any View object. If they are not overridden, then the result is an - * invisible drag shadow. + * reference to any View object. */ public DragShadowBuilder() { mView = new WeakReference(null); @@ -23697,6 +23706,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // Create 1x1 surface when zero surface size is specified because SurfaceControl.Builder // does not accept zero size surface. if (shadowSize.x == 0 || shadowSize.y == 0) { + if (!sAcceptZeroSizeDragShadow) { + throw new IllegalStateException("Drag shadow dimensions must be positive"); + } shadowSize.x = 1; shadowSize.y = 1; } -- GitLab From d1d0e4ae1a57bc9b0966c6b90706278fffbe7418 Mon Sep 17 00:00:00 2001 From: felipeal Date: Mon, 5 Feb 2018 15:45:41 +0100 Subject: [PATCH 036/671] Dump autofill compat packages. Test: manual verification Bug:72811034 Change-Id: Iee815a84e4ff9fa1ace9301b5da7c883d4e753b8 --- .../service/autofill/AutofillServiceInfo.java | 10 ++++++++++ .../autofill/AutofillManagerServiceImpl.java | 20 ++++++++++++++----- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/core/java/android/service/autofill/AutofillServiceInfo.java b/core/java/android/service/autofill/AutofillServiceInfo.java index 29c6cea1498..7383de738ad 100644 --- a/core/java/android/service/autofill/AutofillServiceInfo.java +++ b/core/java/android/service/autofill/AutofillServiceInfo.java @@ -44,6 +44,7 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; +import java.io.PrintWriter; import java.util.Map; /** @@ -247,4 +248,13 @@ public final class AutofillServiceInfo { && !mCompatibilityPackages.isEmpty()).append("]"); return builder.toString(); } + + /** + * Dumps it! + */ + public void dump(String prefix, PrintWriter pw) { + pw.print(prefix); pw.print("Component: "); pw.println(getServiceInfo().getComponentName()); + pw.print(prefix); pw.print("Settings: "); pw.println(mSettingsActivity); + pw.print(prefix); pw.print("Compat packages: "); pw.println(mCompatibilityPackages); + } } diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index 297dcf16c17..989a7b5619d 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -861,8 +861,13 @@ final class AutofillManagerServiceImpl { pw.print(prefix); pw.print("User: "); pw.println(mUserId); pw.print(prefix); pw.print("UID: "); pw.println(getServiceUidLocked()); - pw.print(prefix); pw.print("Component: "); pw.println(mInfo != null - ? mInfo.getServiceInfo().getComponentName() : null); + pw.print(prefix); pw.print("Autofill Service Info: "); + if (mInfo == null) { + pw.println("N/A"); + } else { + pw.println(); + mInfo.dump(prefix2, pw); + } pw.print(prefix); pw.print("Component from settings: "); pw.println(getComponentNameFromSettings()); pw.print(prefix); pw.print("Default component: "); @@ -870,6 +875,7 @@ final class AutofillManagerServiceImpl { pw.print(prefix); pw.print("Disabled: "); pw.println(mDisabled); pw.print(prefix); pw.print("Field classification enabled: "); pw.println(isFieldClassificationEnabledLocked()); + pw.print(prefix); pw.print("Compat pkgs: "); pw.println(getWhitelistedCompatModePackages()); pw.print(prefix); pw.print("Setup complete: "); pw.println(mSetupComplete); pw.print(prefix); pw.print("Last prune: "); pw.println(mLastPrune); @@ -996,14 +1002,18 @@ final class AutofillManagerServiceImpl { } if (!Build.IS_ENG) { // TODO: Build a map and watch for settings changes (this is called on app start) - final String whiteListedPackages = Settings.Global.getString( - mContext.getContentResolver(), - Settings.Global.AUTOFILL_COMPAT_ALLOWED_PACKAGES); + final String whiteListedPackages = getWhitelistedCompatModePackages(); return whiteListedPackages != null && whiteListedPackages.contains(packageName); } return true; } + private String getWhitelistedCompatModePackages() { + return Settings.Global.getString( + mContext.getContentResolver(), + Settings.Global.AUTOFILL_COMPAT_ALLOWED_PACKAGES); + } + private void sendStateToClients(boolean resetClient) { final RemoteCallbackList clients; final int userClientCount; -- GitLab From c1313eb44d01285983975bd57f010c526ca2ff56 Mon Sep 17 00:00:00 2001 From: Beverly Date: Wed, 31 Jan 2018 18:07:21 -0500 Subject: [PATCH 037/671] Wireless charging sound used for wired charging Test: manual (plug/unplug with charging sounds enabled) Change-Id: Ic97cd15421804ff4b6edacfc20dd81515835d58d Fixes: 70259821 Bug: 29737261 --- core/java/android/provider/Settings.java | 4 +-- .../android/provider/SettingsBackupTest.java | 4 +-- .../providers/settings/DatabaseHelper.java | 5 ++- .../settings/SettingsProtoDumpUtil.java | 2 +- .../com/android/server/power/Notifier.java | 31 +++++++++++++++++-- .../server/power/PowerManagerService.java | 4 +++ 6 files changed, 38 insertions(+), 12 deletions(-) diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 6f62f6baa96..b73df9d2198 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -8373,10 +8373,10 @@ public final class Settings { private static final Validator POWER_SOUNDS_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR; /** - * URI for the "wireless charging started" sound. + * URI for the "wireless charging started" and "wired charging started" sound. * @hide */ - public static final String WIRELESS_CHARGING_STARTED_SOUND = + public static final String CHARGING_STARTED_SOUND = "wireless_charging_started_sound"; /** diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index 6602bf6aca0..ed356d04539 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -17,11 +17,9 @@ package android.provider; import static com.google.android.collect.Sets.newHashSet; - import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.is; - import static java.lang.reflect.Modifier.isFinal; import static java.lang.reflect.Modifier.isPublic; import static java.lang.reflect.Modifier.isStatic; @@ -440,7 +438,7 @@ public class SettingsBackupTest { Settings.Global.WIFI_WATCHDOG_ON, Settings.Global.WIMAX_NETWORKS_AVAILABLE_NOTIFICATION_ON, Settings.Global.WINDOW_ANIMATION_SCALE, - Settings.Global.WIRELESS_CHARGING_STARTED_SOUND, + Settings.Global.CHARGING_STARTED_SOUND, Settings.Global.WTF_IS_FATAL, Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_CONFIG_ETAG, diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java index 1dc8e46a694..cfcfb95b0f8 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java @@ -43,7 +43,6 @@ import android.provider.Settings.Secure; import android.text.TextUtils; import android.util.Log; -import com.android.ims.ImsConfig; import com.android.internal.content.PackageHelper; import com.android.internal.telephony.Phone; import com.android.internal.telephony.RILConstants; @@ -1504,7 +1503,7 @@ class DatabaseHelper extends SQLiteOpenHelper { try { stmt = db.compileStatement("INSERT OR REPLACE INTO global(name,value)" + " VALUES(?,?);"); - loadStringSetting(stmt, Settings.Global.WIRELESS_CHARGING_STARTED_SOUND, + loadStringSetting(stmt, Settings.Global.CHARGING_STARTED_SOUND, R.string.def_wireless_charging_started_sound); db.setTransactionSuccessful(); } finally { @@ -2578,7 +2577,7 @@ class DatabaseHelper extends SQLiteOpenHelper { R.string.def_car_dock_sound); loadStringSetting(stmt, Settings.Global.CAR_UNDOCK_SOUND, R.string.def_car_undock_sound); - loadStringSetting(stmt, Settings.Global.WIRELESS_CHARGING_STARTED_SOUND, + loadStringSetting(stmt, Settings.Global.CHARGING_STARTED_SOUND, R.string.def_wireless_charging_started_sound); loadIntegerSetting(stmt, Settings.Global.DOCK_AUDIO_MEDIA_ENABLED, diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index 537e8dca39c..39f2b5293da 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -160,7 +160,7 @@ class SettingsProtoDumpUtil { Settings.Global.POWER_SOUNDS_ENABLED, GlobalSettingsProto.POWER_SOUNDS_ENABLED); dumpSetting(s, p, - Settings.Global.WIRELESS_CHARGING_STARTED_SOUND, + Settings.Global.CHARGING_STARTED_SOUND, GlobalSettingsProto.WIRELESS_CHARGING_STARTED_SOUND); dumpSetting(s, p, Settings.Global.CHARGING_SOUNDS_ENABLED, diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java index a53627093df..3072f212d7c 100644 --- a/services/core/java/com/android/server/power/Notifier.java +++ b/services/core/java/com/android/server/power/Notifier.java @@ -85,6 +85,7 @@ final class Notifier { private static final int MSG_WIRELESS_CHARGING_STARTED = 3; private static final int MSG_SCREEN_BRIGHTNESS_BOOST_CHANGED = 4; private static final int MSG_PROFILE_TIMED_OUT = 5; + private static final int MSG_WIRED_CHARGING_STARTED = 6; private final Object mLock = new Object(); @@ -571,6 +572,20 @@ final class Notifier { mHandler.sendMessage(msg); } + /** + * Called when wired charging has started so as to provide user feedback + */ + public void onWiredChargingStarted() { + if (DEBUG) { + Slog.d(TAG, "onWiredChargingStarted"); + } + + mSuspendBlocker.acquire(); + Message msg = mHandler.obtainMessage(MSG_WIRED_CHARGING_STARTED); + msg.setAsynchronous(true); + mHandler.sendMessage(msg); + } + private void updatePendingBroadcastLocked() { if (!mBroadcastInProgress && mPendingInteractiveState != INTERACTIVE_STATE_UNKNOWN @@ -703,11 +718,14 @@ final class Notifier { } }; - private void playWirelessChargingStartedSound() { + /** + * Plays the wireless charging sound for both wireless and non-wireless charging + */ + private void playChargingStartedSound() { final boolean enabled = Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.CHARGING_SOUNDS_ENABLED, 1) != 0; final String soundPath = Settings.Global.getString(mContext.getContentResolver(), - Settings.Global.WIRELESS_CHARGING_STARTED_SOUND); + Settings.Global.CHARGING_STARTED_SOUND); if (enabled && soundPath != null) { final Uri soundUri = Uri.parse("file://" + soundPath); if (soundUri != null) { @@ -721,11 +739,16 @@ final class Notifier { } private void showWirelessChargingStarted(int batteryLevel) { - playWirelessChargingStartedSound(); + playChargingStartedSound(); mStatusBarManagerInternal.showChargingAnimation(batteryLevel); mSuspendBlocker.release(); } + private void showWiredChargingStarted() { + playChargingStartedSound(); + mSuspendBlocker.release(); + } + private void lockProfile(@UserIdInt int userId) { mTrustManager.setDeviceLockedForUser(userId, true /*locked*/); } @@ -753,6 +776,8 @@ final class Notifier { case MSG_PROFILE_TIMED_OUT: lockProfile(msg.arg1); break; + case MSG_WIRED_CHARGING_STARTED: + showWiredChargingStarted(); } } } diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index db831581c79..1bb85c4426e 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -1747,6 +1747,10 @@ public final class PowerManagerService extends SystemService // it can provide feedback to the user. if (dockedOnWirelessCharger || DEBUG_WIRELESS) { mNotifier.onWirelessChargingStarted(mBatteryLevel); + } else if (mIsPowered && !wasPowered + && (mPlugType == BatteryManager.BATTERY_PLUGGED_AC + || mPlugType == BatteryManager.BATTERY_PLUGGED_USB)) { + mNotifier.onWiredChargingStarted(); } } -- GitLab From 42151571e0e3010049c7d3124121c21fb403372d Mon Sep 17 00:00:00 2001 From: Narayan Kamath Date: Mon, 5 Feb 2018 15:29:04 +0000 Subject: [PATCH 038/671] BatteryStats: Fix bogus calls to addHistoryRecordLocked. The second argument (byte cmd) was being cast to long and being used as uptimeMs. Test: BatteryStatsTests Bug: 71355449 Change-Id: If40745d25038cd5d206244f7a28d6902bbb43c18 --- core/java/com/android/internal/os/BatteryStatsImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 40dcf25bbd1..cf3e073a8cb 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -3743,7 +3743,7 @@ public class BatteryStatsImpl extends BatteryStats { if (mNumHistoryItems == MAX_HISTORY_ITEMS || mNumHistoryItems == MAX_MAX_HISTORY_ITEMS) { - addHistoryRecordLocked(elapsedRealtimeMs, HistoryItem.CMD_OVERFLOW); + addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_OVERFLOW, cur); } if (mNumHistoryItems >= MAX_HISTORY_ITEMS) { @@ -3760,7 +3760,7 @@ public class BatteryStatsImpl extends BatteryStats { } } - addHistoryRecordLocked(elapsedRealtimeMs, HistoryItem.CMD_UPDATE); + addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE, cur); } public void addHistoryEventLocked(long elapsedRealtimeMs, long uptimeMs, int code, -- GitLab From 92c6decbb8e39f9b5ffae38528f47fcebabf7d44 Mon Sep 17 00:00:00 2001 From: Jan Althaus Date: Fri, 2 Feb 2018 09:20:14 +0100 Subject: [PATCH 039/671] Logging for linkify selections Bug: 67629726, 70246800 Test: Manually validated, ran CTS tests, and framework tests Change-Id: Icd41f1e171767bc466f47c87a6ab611185745fd4 --- api/current.txt | 5 +++- .../textclassifier/logging/DefaultLogger.java | 30 +++++++++++++++++-- .../view/textclassifier/logging/Logger.java | 21 ++++++++----- .../logging/SelectionEvent.java | 21 ++++++++++++- .../widget/SelectionActionModeHelper.java | 21 +++++++++---- 5 files changed, 80 insertions(+), 18 deletions(-) diff --git a/api/current.txt b/api/current.txt index 5a3bc1a37cc..694ac8c6578 100644 --- a/api/current.txt +++ b/api/current.txt @@ -50292,7 +50292,7 @@ package android.view.textclassifier.logging { method public final void logSelectionModifiedEvent(int, int); method public final void logSelectionModifiedEvent(int, int, android.view.textclassifier.TextClassification); method public final void logSelectionModifiedEvent(int, int, android.view.textclassifier.TextSelection); - method public final void logSelectionStartedEvent(int); + method public final void logSelectionStartedEvent(int, int); method public abstract void writeEvent(android.view.textclassifier.logging.SelectionEvent); field public static final int OUT_OF_BOUNDS = 2147483647; // 0x7fffffff field public static final int OUT_OF_BOUNDS_NEGATIVE = -2147483648; // 0x80000000 @@ -50322,6 +50322,7 @@ package android.view.textclassifier.logging { method public int getEventIndex(); method public long getEventTime(); method public int getEventType(); + method public int getInvocationMethod(); method public java.lang.String getPackageName(); method public java.lang.String getSessionId(); method public java.lang.String getSignature(); @@ -50346,6 +50347,8 @@ package android.view.textclassifier.logging { field public static final int EVENT_SELECTION_STARTED = 1; // 0x1 field public static final int EVENT_SMART_SELECTION_MULTI = 4; // 0x4 field public static final int EVENT_SMART_SELECTION_SINGLE = 3; // 0x3 + field public static final int INVOCATION_LINK = 2; // 0x2 + field public static final int INVOCATION_MANUAL = 1; // 0x1 } } diff --git a/core/java/android/view/textclassifier/logging/DefaultLogger.java b/core/java/android/view/textclassifier/logging/DefaultLogger.java index 6b848351cbf..0fd4ab9a88f 100644 --- a/core/java/android/view/textclassifier/logging/DefaultLogger.java +++ b/core/java/android/view/textclassifier/logging/DefaultLogger.java @@ -79,7 +79,7 @@ public final class DefaultLogger extends Logger { Preconditions.checkNotNull(event); final LogMaker log = new LogMaker(MetricsEvent.TEXT_SELECTION_SESSION) .setType(getLogType(event)) - .setSubtype(MetricsEvent.TEXT_SELECTION_INVOCATION_MANUAL) + .setSubtype(getLogSubType(event)) .setPackageName(event.getPackageName()) .addTaggedData(START_EVENT_DELTA, event.getDurationSinceSessionStart()) .addTaggedData(PREV_EVENT_DELTA, event.getDurationSincePreviousEvent()) @@ -136,6 +136,17 @@ public final class DefaultLogger extends Logger { } } + private static int getLogSubType(SelectionEvent event) { + switch (event.getInvocationMethod()) { + case SelectionEvent.INVOCATION_MANUAL: + return MetricsEvent.TEXT_SELECTION_INVOCATION_MANUAL; + case SelectionEvent.INVOCATION_LINK: + return MetricsEvent.TEXT_SELECTION_INVOCATION_LINK; + default: + return MetricsEvent.TEXT_SELECTION_INVOCATION_UNKNOWN; + } + } + private static String getLogTypeString(int logType) { switch (logType) { case MetricsEvent.ACTION_TEXT_SELECTION_OVERTYPE: @@ -175,6 +186,17 @@ public final class DefaultLogger extends Logger { } } + private static String getLogSubTypeString(int logSubType) { + switch (logSubType) { + case MetricsEvent.TEXT_SELECTION_INVOCATION_MANUAL: + return "MANUAL"; + case MetricsEvent.TEXT_SELECTION_INVOCATION_LINK: + return "LINK"; + default: + return UNKNOWN; + } + } + private static void debugLog(LogMaker log) { if (!DEBUG_LOG_ENABLED) return; @@ -192,6 +214,7 @@ public final class DefaultLogger extends Logger { final String model = Objects.toString(log.getTaggedData(MODEL_NAME), UNKNOWN); final String entity = Objects.toString(log.getTaggedData(ENTITY_TYPE), UNKNOWN); final String type = getLogTypeString(log.getType()); + final String subType = getLogSubTypeString(log.getSubtype()); final int smartStart = Integer.parseInt( Objects.toString(log.getTaggedData(SMART_START), ZERO)); final int smartEnd = Integer.parseInt( @@ -201,8 +224,9 @@ public final class DefaultLogger extends Logger { final int eventEnd = Integer.parseInt( Objects.toString(log.getTaggedData(EVENT_END), ZERO)); - Log.d(LOG_TAG, String.format("%2d: %s/%s, range=%d,%d - smart_range=%d,%d (%s/%s)", - index, type, entity, eventStart, eventEnd, smartStart, smartEnd, widget, model)); + Log.d(LOG_TAG, String.format("%2d: %s/%s/%s, range=%d,%d - smart_range=%d,%d (%s/%s)", + index, type, subType, entity, eventStart, eventEnd, smartStart, smartEnd, widget, + model)); } /** diff --git a/core/java/android/view/textclassifier/logging/Logger.java b/core/java/android/view/textclassifier/logging/Logger.java index 40e4d8ce1a7..4448b2b5b49 100644 --- a/core/java/android/view/textclassifier/logging/Logger.java +++ b/core/java/android/view/textclassifier/logging/Logger.java @@ -71,6 +71,7 @@ public abstract class Logger { public static final String WIDGET_CUSTOM_UNSELECTABLE_TEXTVIEW = "nosel-customview"; public static final String WIDGET_UNKNOWN = "unknown"; + private @SelectionEvent.InvocationMethod int mInvocationMethod; private SelectionEvent mPrevEvent; private SelectionEvent mSmartEvent; private SelectionEvent mStartEvent; @@ -124,16 +125,19 @@ public abstract class Logger { /** * Logs a "selection started" event. * + * @param invocationMethod the way the selection was triggered * @param start the token index of the selected token */ - public final void logSelectionStartedEvent(int start) { + public final void logSelectionStartedEvent( + @SelectionEvent.InvocationMethod int invocationMethod, int start) { if (mConfig == null) { return; } + mInvocationMethod = invocationMethod; logEvent(new SelectionEvent( start, start + 1, SelectionEvent.EVENT_SELECTION_STARTED, - TextClassifier.TYPE_UNKNOWN, NO_SIGNATURE, mConfig)); + TextClassifier.TYPE_UNKNOWN, mInvocationMethod, NO_SIGNATURE, mConfig)); } /** @@ -152,7 +156,7 @@ public abstract class Logger { logEvent(new SelectionEvent( start, end, SelectionEvent.EVENT_SELECTION_MODIFIED, - TextClassifier.TYPE_UNKNOWN, NO_SIGNATURE, mConfig)); + TextClassifier.TYPE_UNKNOWN, mInvocationMethod, NO_SIGNATURE, mConfig)); } /** @@ -179,7 +183,7 @@ public abstract class Logger { final String signature = classification.getSignature(); logEvent(new SelectionEvent( start, end, SelectionEvent.EVENT_SELECTION_MODIFIED, - entityType, signature, mConfig)); + entityType, mInvocationMethod, signature, mConfig)); } /** @@ -213,7 +217,8 @@ public abstract class Logger { ? selection.getEntity(0) : TextClassifier.TYPE_UNKNOWN; final String signature = selection.getSignature(); - logEvent(new SelectionEvent(start, end, eventType, entityType, signature, mConfig)); + logEvent(new SelectionEvent(start, end, eventType, entityType, mInvocationMethod, signature, + mConfig)); } /** @@ -234,7 +239,8 @@ public abstract class Logger { } logEvent(new SelectionEvent( - start, end, actionType, TextClassifier.TYPE_UNKNOWN, NO_SIGNATURE, mConfig)); + start, end, actionType, TextClassifier.TYPE_UNKNOWN, mInvocationMethod, + NO_SIGNATURE, mConfig)); } /** @@ -265,7 +271,8 @@ public abstract class Logger { ? classification.getEntity(0) : TextClassifier.TYPE_UNKNOWN; final String signature = classification.getSignature(); - logEvent(new SelectionEvent(start, end, actionType, entityType, signature, mConfig)); + logEvent(new SelectionEvent(start, end, actionType, entityType, mInvocationMethod, + signature, mConfig)); } private void logEvent(@NonNull SelectionEvent event) { diff --git a/core/java/android/view/textclassifier/logging/SelectionEvent.java b/core/java/android/view/textclassifier/logging/SelectionEvent.java index f40b6557114..a8de3088d8c 100644 --- a/core/java/android/view/textclassifier/logging/SelectionEvent.java +++ b/core/java/android/view/textclassifier/logging/SelectionEvent.java @@ -98,6 +98,16 @@ public final class SelectionEvent { /** Something else other than User or the default TextClassifier triggered a selection. */ public static final int EVENT_AUTO_SELECTION = 5; + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({INVOCATION_MANUAL, INVOCATION_LINK}) + public @interface InvocationMethod {} + + /** Selection was invoked by the user long pressing, double tapping, or dragging to select. */ + public static final int INVOCATION_MANUAL = 1; + /** Selection was invoked by the user tapping on a link. */ + public static final int INVOCATION_LINK = 2; + private final int mAbsoluteStart; private final int mAbsoluteEnd; private final @EventType int mEventType; @@ -105,6 +115,7 @@ public final class SelectionEvent { @Nullable private final String mWidgetVersion; private final String mPackageName; private final String mWidgetType; + private final @InvocationMethod int mInvocationMethod; // These fields should only be set by creator of a SelectionEvent. private String mSignature; @@ -121,7 +132,7 @@ public final class SelectionEvent { SelectionEvent( int start, int end, @EventType int eventType, @EntityType String entityType, - String signature, Logger.Config config) { + @InvocationMethod int invocationMethod, String signature, Logger.Config config) { Preconditions.checkArgument(end >= start, "end cannot be less than start"); mAbsoluteStart = start; mAbsoluteEnd = end; @@ -132,6 +143,7 @@ public final class SelectionEvent { mWidgetVersion = config.getWidgetVersion(); mPackageName = Preconditions.checkNotNull(config.getPackageName()); mWidgetType = Preconditions.checkNotNull(config.getWidgetType()); + mInvocationMethod = invocationMethod; } int getAbsoluteStart() { @@ -179,6 +191,13 @@ public final class SelectionEvent { return mWidgetVersion; } + /** + * Returns the way the selection mode was invoked. + */ + public @InvocationMethod int getInvocationMethod() { + return mInvocationMethod; + } + /** * Returns the signature of the text classifier result associated with this event. */ diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java index 2e354c1eee1..9ff5f591067 100644 --- a/core/java/android/widget/SelectionActionModeHelper.java +++ b/core/java/android/widget/SelectionActionModeHelper.java @@ -111,7 +111,8 @@ public final class SelectionActionModeHelper { mSelectionTracker.onOriginalSelection( getText(mTextView), mTextView.getSelectionStart(), - mTextView.getSelectionEnd()); + mTextView.getSelectionEnd(), + false /*isLink*/); cancelAsyncTask(); if (skipTextClassification()) { startSelectionActionMode(null); @@ -134,7 +135,11 @@ public final class SelectionActionModeHelper { * Starts Link ActionMode. */ public void startLinkActionModeAsync(TextLinks.TextLink textLink) { - //TODO: tracking/logging + mSelectionTracker.onOriginalSelection( + getText(mTextView), + mTextView.getSelectionStart(), + mTextView.getSelectionEnd(), + true /*isLink*/); cancelAsyncTask(); if (skipTextClassification()) { startLinkActionMode(null); @@ -483,7 +488,8 @@ public final class SelectionActionModeHelper { /** * Called when the original selection happens, before smart selection is triggered. */ - public void onOriginalSelection(CharSequence text, int selectionStart, int selectionEnd) { + public void onOriginalSelection( + CharSequence text, int selectionStart, int selectionEnd, boolean isLink) { // If we abandoned a selection and created a new one very shortly after, we may still // have a pending request to log ABANDON, which we flush here. mDelayedLogAbandon.flush(); @@ -492,7 +498,8 @@ public final class SelectionActionModeHelper { mOriginalEnd = mSelectionEnd = selectionEnd; mAllowReset = false; maybeInvalidateLogger(); - mLogger.logSelectionStarted(text, selectionStart); + mLogger.logSelectionStarted(text, selectionStart, + isLink ? SelectionEvent.INVOCATION_LINK : SelectionEvent.INVOCATION_MANUAL); } /** @@ -675,7 +682,9 @@ public final class SelectionActionModeHelper { return Logger.WIDGET_UNSELECTABLE_TEXTVIEW; } - public void logSelectionStarted(CharSequence text, int index) { + public void logSelectionStarted( + CharSequence text, int index, + @SelectionEvent.InvocationMethod int invocationMethod) { try { Preconditions.checkNotNull(text); Preconditions.checkArgumentInRange(index, 0, text.length(), "index"); @@ -684,7 +693,7 @@ public final class SelectionActionModeHelper { } mTokenIterator.setText(mText); mStartIndex = index; - mLogger.logSelectionStartedEvent(0); + mLogger.logSelectionStartedEvent(invocationMethod, 0); } catch (Exception e) { // Avoid crashes due to logging. Log.d(LOG_TAG, e.getMessage()); -- GitLab From e0d711f4350787aefe07854ca1f42747d5eb96ae Mon Sep 17 00:00:00 2001 From: Julia Reynolds Date: Fri, 1 Sep 2017 08:50:47 -0400 Subject: [PATCH 040/671] Grant uri access to notification listeners. Bug: 9069730 Bug: 63708826 Test: runtest systemui-notification Change-Id: I87e9dffde35e1afc5e23137df09e0c093d656e23 --- core/java/android/app/Notification.java | 7 +- .../java/android/app/NotificationManager.java | 12 +++ .../server/notification/ManagedServices.java | 8 +- .../NotificationManagerService.java | 93 ++++++++++++++++--- .../notification/NotificationRecord.java | 39 ++++++++ .../NotificationManagerServiceTest.java | 80 +++++++++++++++- 6 files changed, 218 insertions(+), 21 deletions(-) diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 6e4098646b2..8393caa33e3 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -1055,11 +1055,10 @@ public class Notification implements Parcelable /** * {@link #extras} key: A * {@link android.content.ContentUris content URI} pointing to an image that can be displayed - * in the background when the notification is selected. The URI must point to an image stream - * suitable for passing into + * in the background when the notification is selected. Used on television platforms. + * The URI must point to an image stream suitable for passing into * {@link android.graphics.BitmapFactory#decodeStream(java.io.InputStream) - * BitmapFactory.decodeStream}; all other content types will be ignored. The content provider - * URI used for this purpose must require no permissions to read the image data. + * BitmapFactory.decodeStream}; all other content types will be ignored. */ public static final String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri"; diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 49c03ab9cfa..f6411936571 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -24,6 +24,7 @@ import android.annotation.TestApi; import android.app.Notification.Builder; import android.content.ComponentName; import android.content.Context; +import android.content.Intent; import android.content.pm.ParceledListSlice; import android.graphics.drawable.Icon; import android.net.Uri; @@ -343,6 +344,14 @@ public class NotificationManager { * the same tag and id has already been posted by your application and has not yet been * canceled, it will be replaced by the updated information. * + * All {@link android.service.notification.NotificationListenerService listener services} will + * be granted {@link Intent#FLAG_GRANT_READ_URI_PERMISSION} access to any {@link Uri uris} + * provided on this notification or the + * {@link NotificationChannel} this notification is posted to using + * {@link Context#grantUriPermission(String, Uri, int)}. Permission will be revoked when the + * notification is canceled, or you can revoke permissions with + * {@link Context#revokeUriPermission(Uri, int)}. + * * @param tag A string identifier for this notification. May be {@code null}. * @param id An identifier for this notification. The pair (tag, id) must be unique * within your application. @@ -363,11 +372,13 @@ public class NotificationManager { String pkg = mContext.getPackageName(); // Fix the notification as best we can. Notification.addFieldsFromContext(mContext, notification); + if (notification.sound != null) { notification.sound = notification.sound.getCanonicalUri(); if (StrictMode.vmFileUriExposureEnabled()) { notification.sound.checkFileUriExposed("Notification.sound"); } + } fixLegacySmallIcon(notification, pkg); if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) { @@ -378,6 +389,7 @@ public class NotificationManager { } if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")"); notification.reduceImageSizes(mContext); + ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE); boolean isLowRam = am.isLowRamDevice(); final Notification copy = Builder.maybeCloneStrippedForDelivery(notification, isLowRam); diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java index fd435f952c1..b7842d5365b 100644 --- a/services/core/java/com/android/server/notification/ManagedServices.java +++ b/services/core/java/com/android/server/notification/ManagedServices.java @@ -376,9 +376,7 @@ abstract public class ManagedServices { protected void upgradeXml(final int xmlVersion, final int userId) {} private void loadAllowedComponentsFromSettings() { - - UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); - for (UserInfo user : userManager.getUsers()) { + for (UserInfo user : mUm.getUsers()) { final ContentResolver cr = mContext.getContentResolver(); addApprovedList(Settings.Secure.getStringForUser( cr, @@ -482,7 +480,9 @@ abstract public class ManagedServices { for (int i = 0; i < allowedByType.size(); i++) { final ArraySet allowed = allowedByType.valueAt(i); allowedPackages.addAll( - allowed.stream().map(this::getPackageName).collect(Collectors.toList())); + allowed.stream().map(this::getPackageName). + filter(value -> !TextUtils.isEmpty(value)) + .collect(Collectors.toList())); } return allowedPackages; } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index b25124ac456..c09bdb4db22 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -25,7 +25,9 @@ import static android.app.NotificationManager.IMPORTANCE_NONE; import static android.content.pm.PackageManager.FEATURE_LEANBACK; import static android.content.pm.PackageManager.FEATURE_TELEVISION; import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.os.UserHandle.USER_ALL; import static android.os.UserHandle.USER_NULL; +import static android.os.UserHandle.USER_SYSTEM; import static android.service.notification.NotificationListenerService .HINT_HOST_DISABLE_CALL_EFFECTS; import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS; @@ -88,6 +90,7 @@ import android.app.usage.UsageStatsManagerInternal; import android.companion.ICompanionDeviceManager; import android.content.BroadcastReceiver; import android.content.ComponentName; +import android.content.ContentProvider; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; @@ -115,6 +118,7 @@ import android.os.IDeviceIdleController; import android.os.IInterface; import android.os.Looper; import android.os.Message; +import android.os.Parcelable; import android.os.Process; import android.os.RemoteException; import android.os.ResultReceiver; @@ -206,6 +210,7 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map.Entry; @@ -288,6 +293,7 @@ public class NotificationManagerService extends SystemService { private ICompanionDeviceManager mCompanionManager; private AccessibilityManager mAccessibilityManager; private IDeviceIdleController mDeviceIdleController; + private IBinder mPermissionOwner; final IBinder mForegroundToken = new Binder(); private WorkerHandler mHandler; @@ -524,7 +530,7 @@ public class NotificationManagerService extends SystemService { } catch (FileNotFoundException e) { // No data yet // Load default managed services approvals - readDefaultApprovedServices(UserHandle.USER_SYSTEM); + readDefaultApprovedServices(USER_SYSTEM); } catch (IOException e) { Log.wtf(TAG, "Unable to read notification policy", e); } catch (NumberFormatException e) { @@ -974,7 +980,7 @@ public class NotificationManagerService extends SystemService { final int enabled = mPackageManager.getApplicationEnabledSetting( pkgName, changeUserId != UserHandle.USER_ALL ? changeUserId : - UserHandle.USER_SYSTEM); + USER_SYSTEM); if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) { cancelNotifications = false; @@ -1061,6 +1067,7 @@ public class NotificationManagerService extends SystemService { } } else if (action.equals(Intent.ACTION_USER_REMOVED)) { final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL); + mUserProfiles.updateCache(context); mZenModeHelper.onUserRemoved(user); mRankingHelper.onUserRemoved(user); mListeners.onUserRemoved(user); @@ -1268,7 +1275,7 @@ public class NotificationManagerService extends SystemService { NotificationAssistants notificationAssistants, ConditionProviders conditionProviders, ICompanionDeviceManager companionManager, SnoozeHelper snoozeHelper, NotificationUsageStats usageStats, AtomicFile policyFile, - ActivityManager activityManager, GroupHelper groupHelper) { + ActivityManager activityManager, GroupHelper groupHelper, IActivityManager am) { Resources resources = getContext().getResources(); mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(), Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE, @@ -1276,7 +1283,7 @@ public class NotificationManagerService extends SystemService { mAccessibilityManager = (AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE); - mAm = ActivityManager.getService(); + mAm = am; mPackageManager = packageManager; mPackageManagerClient = packageManagerClient; mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE); @@ -1287,6 +1294,11 @@ public class NotificationManagerService extends SystemService { mActivityManager = activityManager; mDeviceIdleController = IDeviceIdleController.Stub.asInterface( ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER)); + try { + mPermissionOwner = mAm.newUriPermissionOwner("notification"); + } catch (RemoteException e) { + Slog.w(TAG, "AM dead", e); + } mHandler = new WorkerHandler(looper); mRankingThread.start(); @@ -1415,7 +1427,7 @@ public class NotificationManagerService extends SystemService { null, snoozeHelper, new NotificationUsageStats(getContext()), new AtomicFile(new File(systemDir, "notification_policy.xml"), "notification-policy"), (ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE), - getGroupHelper()); + getGroupHelper(), ActivityManager.getService()); // register for various Intents IntentFilter filter = new IntentFilter(); @@ -1749,7 +1761,7 @@ public class NotificationManagerService extends SystemService { protected void reportSeen(NotificationRecord r) { final int userId = r.sbn.getUserId(); mAppUsageStats.reportEvent(r.sbn.getPackageName(), - userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM + userId == UserHandle.USER_ALL ? USER_SYSTEM : userId, UsageEvents.Event.NOTIFICATION_SEEN); } @@ -2894,7 +2906,7 @@ public class NotificationManagerService extends SystemService { checkCallerIsSystem(); if (DBG) Slog.d(TAG, "getBackupPayload u=" + user); //TODO: http://b/22388012 - if (user != UserHandle.USER_SYSTEM) { + if (user != USER_SYSTEM) { Slog.w(TAG, "getBackupPayload: cannot backup policy for user " + user); return null; } @@ -2920,7 +2932,7 @@ public class NotificationManagerService extends SystemService { return; } //TODO: http://b/22388012 - if (user != UserHandle.USER_SYSTEM) { + if (user != USER_SYSTEM) { Slog.w(TAG, "applyRestore: cannot restore policy for user " + user); return; } @@ -3678,7 +3690,7 @@ public class NotificationManagerService extends SystemService { sbn.getNotification().flags = (r.mOriginalFlags & ~Notification.FLAG_FOREGROUND_SERVICE); mRankingHelper.sort(mNotificationList); - mListeners.notifyPostedLocked(sbn, sbn /* oldSbn */); + mListeners.notifyPostedLocked(r, sbn /* oldSbn */); } }; @@ -3707,7 +3719,7 @@ public class NotificationManagerService extends SystemService { try { final ApplicationInfo ai = mPackageManagerClient.getApplicationInfoAsUser( pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING, - (userId == UserHandle.USER_ALL) ? UserHandle.USER_SYSTEM : userId); + (userId == UserHandle.USER_ALL) ? USER_SYSTEM : userId); Notification.addFieldsFromContext(ai, notification); int canColorize = mPackageManagerClient.checkPermission( @@ -4126,6 +4138,8 @@ public class NotificationManagerService extends SystemService { // Make sure we don't lose the foreground service state. notification.flags |= old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE; + // revoke uri permissions for changed uris + revokeUriPermissions(r, old); r.isUpdate = true; } @@ -4147,7 +4161,7 @@ public class NotificationManagerService extends SystemService { if (notification.getSmallIcon() != null) { StatusBarNotification oldSbn = (old != null) ? old.sbn : null; - mListeners.notifyPostedLocked(n, oldSbn); + mListeners.notifyPostedLocked(r, oldSbn); if (oldSbn == null || !Objects.equals(oldSbn.getGroup(), n.getGroup())) { mHandler.post(new Runnable() { @Override @@ -4912,6 +4926,9 @@ public class NotificationManagerService extends SystemService { r.recordDismissalSurface(NotificationStats.DISMISSAL_OTHER); } + // Revoke permissions + revokeUriPermissions(null, r); + // tell the app if (sendDelete) { if (r.getNotification().deleteIntent != null) { @@ -5009,6 +5026,30 @@ public class NotificationManagerService extends SystemService { r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now), listenerName); } + void revokeUriPermissions(NotificationRecord newRecord, NotificationRecord oldRecord) { + Set oldUris = oldRecord.getNotificationUris(); + Set newUris = newRecord == null ? new HashSet<>() : newRecord.getNotificationUris(); + oldUris.removeAll(newUris); + + long ident = Binder.clearCallingIdentity(); + try { + for (Uri uri : oldUris) { + if (uri != null) { + int notiUserId = oldRecord.getUserId(); + int sourceUserId = notiUserId == USER_ALL ? USER_SYSTEM + : ContentProvider.getUserIdFromUri(uri, notiUserId); + uri = ContentProvider.getUriWithoutUserId(uri); + mAm.revokeUriPermissionFromOwner(mPermissionOwner, + uri, Intent.FLAG_GRANT_READ_URI_PERMISSION, sourceUserId); + } + } + } catch (RemoteException e) { + Log.e(TAG, "Count not revoke uri permissions", e); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + /** * Cancels a notification ONLY if it has all of the {@code mustHaveFlags} * and none of the {@code mustNotHaveFlags}. @@ -5851,10 +5892,13 @@ public class NotificationManagerService extends SystemService { * but isn't anymore. */ @GuardedBy("mNotificationLock") - public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) { + public void notifyPostedLocked(NotificationRecord r, StatusBarNotification oldSbn) { // Lazily initialized snapshots of the notification. + StatusBarNotification sbn = r.sbn; TrimCache trimCache = new TrimCache(sbn); + Set uris = r.getNotificationUris(); + for (final ManagedServiceInfo info : getServices()) { boolean sbnVisible = isVisibleToListener(sbn, info); boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false; @@ -5877,6 +5921,9 @@ public class NotificationManagerService extends SystemService { continue; } + grantUriPermissions(uris, sbn.getUserId(), info.component.getPackageName(), + info.userid); + final StatusBarNotification sbnToPost = trimCache.ForListener(info); mHandler.post(new Runnable() { @Override @@ -5887,6 +5934,28 @@ public class NotificationManagerService extends SystemService { } } + private void grantUriPermissions(Set uris, int notiUserId, String listenerPkg, + int listenerUserId) { + long ident = Binder.clearCallingIdentity(); + try { + for (Uri uri : uris) { + if (uri != null) { + int sourceUserId = notiUserId == USER_ALL ? USER_SYSTEM + : ContentProvider.getUserIdFromUri(uri, notiUserId); + uri = ContentProvider.getUriWithoutUserId(uri); + mAm.grantUriPermissionFromOwner(mPermissionOwner, Process.myUid(), + listenerPkg, + uri, Intent.FLAG_GRANT_READ_URI_PERMISSION, sourceUserId, + listenerUserId == USER_ALL ? USER_SYSTEM : listenerUserId); + } + } + } catch (RemoteException e) { + Log.e(TAG, "Count not grant uri permission to " + listenerPkg, e); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + /** * asynchronously notify all listeners about a removed notification */ diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java index 23b9743aaf7..1ad8c74a831 100644 --- a/services/core/java/com/android/server/notification/NotificationRecord.java +++ b/services/core/java/com/android/server/notification/NotificationRecord.java @@ -38,6 +38,7 @@ import android.metrics.LogMaker; import android.net.Uri; import android.os.Build; import android.os.Bundle; +import android.os.Parcelable; import android.os.UserHandle; import android.provider.Settings; import android.service.notification.Adjustment; @@ -47,6 +48,7 @@ import android.service.notification.NotificationStats; import android.service.notification.SnoozeCriterion; import android.service.notification.StatusBarNotification; import android.text.TextUtils; +import android.util.ArraySet; import android.util.Log; import android.util.Slog; import android.util.TimeUtils; @@ -64,6 +66,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Objects; +import java.util.Set; /** * Holds data about notifications that should not be shared with the @@ -929,6 +932,42 @@ public final class NotificationRecord { mStats.setViewedSettings(); } + public Set getNotificationUris() { + Notification notification = getNotification(); + Set uris = new ArraySet<>(); + + if (notification.sound != null) { + uris.add(notification.sound); + } + if (notification.getChannelId() != null) { + NotificationChannel channel = getChannel(); + if (channel != null && channel.getSound() != null) { + uris.add(channel.getSound()); + } + } + if (notification.extras.containsKey(Notification.EXTRA_AUDIO_CONTENTS_URI)) { + uris.add(notification.extras.getParcelable(Notification.EXTRA_AUDIO_CONTENTS_URI)); + } + if (notification.extras.containsKey(Notification.EXTRA_BACKGROUND_IMAGE_URI)) { + uris.add(notification.extras.getParcelable(Notification.EXTRA_BACKGROUND_IMAGE_URI)); + } + if (Notification.MessagingStyle.class.equals(notification.getNotificationStyle())) { + Parcelable[] newMessages = + notification.extras.getParcelableArray(Notification.EXTRA_MESSAGES); + List messages + = Notification.MessagingStyle.Message.getMessagesFromBundleArray(newMessages); + Parcelable[] histMessages = + notification.extras.getParcelableArray(Notification.EXTRA_HISTORIC_MESSAGES); + messages.addAll( + Notification.MessagingStyle.Message.getMessagesFromBundleArray(histMessages)); + for (Notification.MessagingStyle.Message message : messages) { + uris.add(message.getDataUri()); + } + } + + return uris; + } + public LogMaker getLogMaker(long now) { if (mLogMaker == null) { // initialize fields that only change on update (so a new record) diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 6b6df294e47..f82c47521c2 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -38,6 +38,7 @@ import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -49,8 +50,10 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.ActivityManager; +import android.app.IActivityManager; import android.app.INotificationManager; import android.app.Notification; +import android.app.Notification.MessagingStyle.Message; import android.app.NotificationChannel; import android.app.NotificationChannelGroup; import android.app.NotificationManager; @@ -64,9 +67,11 @@ import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; import android.graphics.Color; import android.media.AudioManager; +import android.net.Uri; import android.os.Binder; import android.os.Build; import android.os.Bundle; +import android.os.IBinder; import android.os.Process; import android.os.UserHandle; import android.provider.Settings.Secure; @@ -149,6 +154,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Mock private ICompanionDeviceManager mCompanionMgr; @Mock SnoozeHelper mSnoozeHelper; @Mock GroupHelper mGroupHelper; + @Mock + IBinder mPermOwner; + @Mock + IActivityManager mAm; // Use a Testable subclass so we can simulate calls from the system without failing. private static class TestableNotificationManagerService extends NotificationManagerService { @@ -208,6 +217,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { when(mockLightsManager.getLight(anyInt())).thenReturn(mock(Light.class)); when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL); when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(false); + when(mAm.newUriPermissionOwner(anyString())).thenReturn(mPermOwner); // write to a test file; the system file isn't readable from tests mFile = new File(mContext.getCacheDir(), "test.xml"); @@ -238,7 +248,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mPackageManager, mPackageManagerClient, mockLightsManager, mListeners, mAssistants, mConditionProviders, mCompanionMgr, mSnoozeHelper, mUsageStats, mPolicyFile, mActivityManager, - mGroupHelper); + mGroupHelper, mAm); } catch (SecurityException e) { if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) { throw e; @@ -592,6 +602,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mBinderService.getActiveNotifications(PKG); assertEquals(0, notifs.length); assertEquals(0, mService.getNotificationRecordCount()); + verify(mAm, atLeastOnce()).revokeUriPermissionFromOwner( + any(), any(), anyInt(), anyInt()); } @Test @@ -610,6 +622,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { ArgumentCaptor captor = ArgumentCaptor.forClass(NotificationStats.class); verify(mListeners, times(1)).notifyRemovedLocked(any(), anyInt(), captor.capture()); assertEquals(NotificationStats.DISMISSAL_OTHER, captor.getValue().getDismissalSurface()); + verify(mAm, atLeastOnce()).revokeUriPermissionFromOwner(any(), any(), anyInt(), anyInt()); } @Test @@ -624,6 +637,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mBinderService.getActiveNotifications(sbn.getPackageName()); assertEquals(0, notifs.length); assertEquals(0, mService.getNotificationRecordCount()); + verify(mAm, atLeastOnce()).revokeUriPermissionFromOwner(any(), any(), anyInt(), anyInt()); } @Test @@ -637,6 +651,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mBinderService.getActiveNotifications(sbn.getPackageName()); assertEquals(0, notifs.length); assertEquals(0, mService.getNotificationRecordCount()); + verify(mAm, atLeastOnce()).revokeUriPermissionFromOwner(any(), any(), anyInt(), anyInt()); } @Test @@ -658,6 +673,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { ArgumentCaptor captor = ArgumentCaptor.forClass(NotificationStats.class); verify(mListeners, times(1)).notifyRemovedLocked(any(), anyInt(), captor.capture()); assertEquals(NotificationStats.DISMISSAL_OTHER, captor.getValue().getDismissalSurface()); + verify(mAm, atLeastOnce()).revokeUriPermissionFromOwner(any(), any(), anyInt(), anyInt()); } @Test @@ -676,6 +692,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mBinderService.cancelAllNotifications(PKG, parent.sbn.getUserId()); waitForIdle(); assertEquals(0, mService.getNotificationRecordCount()); + verify(mAm, atLeastOnce()).revokeUriPermissionFromOwner(any(), any(), anyInt(), anyInt()); } @Test @@ -689,6 +706,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { waitForIdle(); assertEquals(0, mService.getNotificationRecordCount()); + verify(mAm, atLeastOnce()).revokeUriPermissionFromOwner(any(), any(), anyInt(), anyInt()); } @Test @@ -2447,4 +2465,64 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mBinderService.getBackupPayload(1); assertEquals(1, mService.countSystemChecks - systemChecks); } + + @Test + public void revokeUriPermissions_update() throws Exception { + NotificationChannel c = new NotificationChannel( + TEST_CHANNEL_ID, TEST_CHANNEL_ID, NotificationManager.IMPORTANCE_DEFAULT); + c.setSound(null, Notification.AUDIO_ATTRIBUTES_DEFAULT); + Message message1 = new Message("", 0, ""); + message1.setData("", Uri.fromParts("old", "", "old stuff")); + Message message2 = new Message("", 1, ""); + message2.setData("", Uri.fromParts("new", "", "new stuff")); + + Notification.Builder nb = new Notification.Builder(mContext, c.getId()) + .setContentTitle("foo") + .setSmallIcon(android.R.drawable.sym_def_app_icon) + .setStyle(new Notification.MessagingStyle("") + .addMessage(message1) + .addMessage(message2)); + StatusBarNotification oldSbn = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0, + nb.build(), new UserHandle(mUid), null, 0); + NotificationRecord oldRecord = + new NotificationRecord(mContext, oldSbn, c); + + Notification.Builder nb1 = new Notification.Builder(mContext, c.getId()) + .setContentTitle("foo") + .setSmallIcon(android.R.drawable.sym_def_app_icon) + .setStyle(new Notification.MessagingStyle("").addMessage(message2)); + StatusBarNotification newSbn = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0, + nb1.build(), new UserHandle(mUid), null, 0); + NotificationRecord newRecord = + new NotificationRecord(mContext, newSbn, c); + + mService.revokeUriPermissions(newRecord, oldRecord); + + verify(mAm, times(1)).revokeUriPermissionFromOwner(any(), eq(message1.getDataUri()), + anyInt(), anyInt()); + } + + @Test + public void revokeUriPermissions_cancel() throws Exception { + NotificationChannel c = new NotificationChannel( + TEST_CHANNEL_ID, TEST_CHANNEL_ID, NotificationManager.IMPORTANCE_DEFAULT); + c.setSound(null, Notification.AUDIO_ATTRIBUTES_DEFAULT); + Message message1 = new Message("", 0, ""); + message1.setData("", Uri.fromParts("old", "", "old stuff")); + + Notification.Builder nb = new Notification.Builder(mContext, c.getId()) + .setContentTitle("foo") + .setSmallIcon(android.R.drawable.sym_def_app_icon) + .setStyle(new Notification.MessagingStyle("") + .addMessage(message1)); + StatusBarNotification oldSbn = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0, + nb.build(), new UserHandle(mUid), null, 0); + NotificationRecord oldRecord = + new NotificationRecord(mContext, oldSbn, c); + + mService.revokeUriPermissions(null, oldRecord); + + verify(mAm, times(1)).revokeUriPermissionFromOwner(any(), eq(message1.getDataUri()), + anyInt(), anyInt()); + } } -- GitLab From 5a5a7fefef8b19aa06a8a32f6dab261947497ec7 Mon Sep 17 00:00:00 2001 From: Narayan Kamath Date: Mon, 5 Feb 2018 17:30:06 +0000 Subject: [PATCH 041/671] BatteryStats : Remove unused upTime parameter. Unused, and made it a lot easier to inadvertently call the wrong function given all the other ints and longs floating around the API. Test: BatteryStatsTests Bug: 71355449 Change-Id: Ie9040c5aa13e620ca2301b02bd71e7afef01b874 --- .../android/internal/os/BatteryStatsImpl.java | 39 +++++++++---------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index cf3e073a8cb..4dff33a3969 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -3522,7 +3522,7 @@ public class BatteryStatsImpl extends BatteryStats { mHistoryLastWritten.cmd = HistoryItem.CMD_NULL; } - void addHistoryBufferLocked(long elapsedRealtimeMs, long uptimeMs, HistoryItem cur) { + void addHistoryBufferLocked(long elapsedRealtimeMs, HistoryItem cur) { if (!mHaveBatteryLevel || !mRecordingHistory) { return; } @@ -3603,8 +3603,8 @@ public class BatteryStatsImpl extends BatteryStats { } else if (dataSize >= MAX_HISTORY_BUFFER) { if (!mHistoryOverflow) { mHistoryOverflow = true; - addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE, cur); - addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_OVERFLOW, cur); + addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_UPDATE, cur); + addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_OVERFLOW, cur); return; } @@ -3642,7 +3642,7 @@ public class BatteryStatsImpl extends BatteryStats { return; } - addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE, cur); + addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_UPDATE, cur); return; } @@ -3650,15 +3650,14 @@ public class BatteryStatsImpl extends BatteryStats { // The history is currently empty; we need it to start with a time stamp. cur.currentTime = System.currentTimeMillis(); if (recordResetDueToOverflow) { - addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_OVERFLOW, cur); + addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_OVERFLOW, cur); } - addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_RESET, cur); + addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_RESET, cur); } - addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE, cur); + addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_UPDATE, cur); } - private void addHistoryBufferLocked(long elapsedRealtimeMs, long uptimeMs, byte cmd, - HistoryItem cur) { + private void addHistoryBufferLocked(long elapsedRealtimeMs, byte cmd, HistoryItem cur) { if (mIteratingHistory) { throw new IllegalStateException("Can't do this while iterating history!"); } @@ -3692,17 +3691,17 @@ public class BatteryStatsImpl extends BatteryStats { mHistoryAddTmp.wakeReasonTag = null; mHistoryAddTmp.eventCode = HistoryItem.EVENT_NONE; mHistoryAddTmp.states &= ~HistoryItem.STATE_CPU_RUNNING_FLAG; - addHistoryRecordInnerLocked(wakeElapsedTime, uptimeMs, mHistoryAddTmp); + addHistoryRecordInnerLocked(wakeElapsedTime, mHistoryAddTmp); } } mHistoryCur.states |= HistoryItem.STATE_CPU_RUNNING_FLAG; mTrackRunningHistoryElapsedRealtime = elapsedRealtimeMs; mTrackRunningHistoryUptime = uptimeMs; - addHistoryRecordInnerLocked(elapsedRealtimeMs, uptimeMs, mHistoryCur); + addHistoryRecordInnerLocked(elapsedRealtimeMs, mHistoryCur); } - void addHistoryRecordInnerLocked(long elapsedRealtimeMs, long uptimeMs, HistoryItem cur) { - addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, cur); + void addHistoryRecordInnerLocked(long elapsedRealtimeMs, HistoryItem cur) { + addHistoryBufferLocked(elapsedRealtimeMs, cur); if (!USE_OLD_HISTORY) { return; @@ -3743,7 +3742,7 @@ public class BatteryStatsImpl extends BatteryStats { if (mNumHistoryItems == MAX_HISTORY_ITEMS || mNumHistoryItems == MAX_MAX_HISTORY_ITEMS) { - addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_OVERFLOW, cur); + addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_OVERFLOW, cur); } if (mNumHistoryItems >= MAX_HISTORY_ITEMS) { @@ -3760,7 +3759,7 @@ public class BatteryStatsImpl extends BatteryStats { } } - addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE, cur); + addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_UPDATE, cur); } public void addHistoryEventLocked(long elapsedRealtimeMs, long uptimeMs, int code, @@ -12307,7 +12306,7 @@ public class BatteryStatsImpl extends BatteryStats { boolean reset) { mRecordingHistory = true; mHistoryCur.currentTime = System.currentTimeMillis(); - addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, + addHistoryBufferLocked(elapsedRealtimeMs, reset ? HistoryItem.CMD_RESET : HistoryItem.CMD_CURRENT_TIME, mHistoryCur); mHistoryCur.currentTime = 0; @@ -12320,8 +12319,7 @@ public class BatteryStatsImpl extends BatteryStats { final long uptimeMs) { if (mRecordingHistory) { mHistoryCur.currentTime = currentTime; - addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_CURRENT_TIME, - mHistoryCur); + addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_CURRENT_TIME, mHistoryCur); mHistoryCur.currentTime = 0; } } @@ -12329,8 +12327,7 @@ public class BatteryStatsImpl extends BatteryStats { private void recordShutdownLocked(final long elapsedRealtimeMs, final long uptimeMs) { if (mRecordingHistory) { mHistoryCur.currentTime = System.currentTimeMillis(); - addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_SHUTDOWN, - mHistoryCur); + addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_SHUTDOWN, mHistoryCur); mHistoryCur.currentTime = 0; } } @@ -13274,7 +13271,7 @@ public class BatteryStatsImpl extends BatteryStats { if (USE_OLD_HISTORY) { addHistoryRecordLocked(elapsedRealtime, uptime, HistoryItem.CMD_START, mHistoryCur); } - addHistoryBufferLocked(elapsedRealtime, uptime, HistoryItem.CMD_START, mHistoryCur); + addHistoryBufferLocked(elapsedRealtime, HistoryItem.CMD_START, mHistoryCur); startRecordingHistory(elapsedRealtime, uptime, false); } -- GitLab From 317b05a055fd7266607ab0ab258cce5bfeb91e8f Mon Sep 17 00:00:00 2001 From: goneil Date: Thu, 7 Dec 2017 16:31:10 -0800 Subject: [PATCH 042/671] Make TelephonyManager#getNai() public Bug: 67750905 Test: android.telephony.cts.TelephonyManagerTest#testTelephonyManager Change-Id: I92af07a5ed2abd852ff0f79909c574d78b89f535 --- api/current.txt | 1 + .../android/telephony/TelephonyManager.java | 19 ++++++++++++++----- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/api/current.txt b/api/current.txt index e047bdc0ef6..30cf1139c73 100644 --- a/api/current.txt +++ b/api/current.txt @@ -40833,6 +40833,7 @@ package android.telephony { method public java.lang.String getMeid(int); method public java.lang.String getMmsUAProfUrl(); method public java.lang.String getMmsUserAgent(); + method public java.lang.String getNai(); method public deprecated java.util.List getNeighboringCellInfo(); method public java.lang.String getNetworkCountryIso(); method public java.lang.String getNetworkOperator(); diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 4138e2950d9..91db547d58a 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -1155,12 +1155,14 @@ public class TelephonyManager { } /** - * Returns the NAI. Return null if NAI is not available. - * + * Returns the Network Access Identifier (NAI). Return null if NAI is not available. + *

+ * Requires Permission: + * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} */ - /** {@hide}*/ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getNai() { - return getNai(getSlotIndex()); + return getNaiBySubscriberId(getSubId()); } /** @@ -1171,11 +1173,18 @@ public class TelephonyManager { /** {@hide}*/ public String getNai(int slotIndex) { int[] subId = SubscriptionManager.getSubId(slotIndex); + if (subId == null) { + return null; + } + return getNaiBySubscriberId(subId[0]); + } + + private String getNaiBySubscriberId(int subId) { try { IPhoneSubInfo info = getSubscriberInfo(); if (info == null) return null; - String nai = info.getNaiForSubscriber(subId[0], mContext.getOpPackageName()); + String nai = info.getNaiForSubscriber(subId, mContext.getOpPackageName()); if (Log.isLoggable(TAG, Log.VERBOSE)) { Rlog.v(TAG, "Nai = " + nai); } -- GitLab From 2d7771ca600e63549b3afc982db44b4cb38a2fd8 Mon Sep 17 00:00:00 2001 From: Alan Viverette Date: Wed, 31 Jan 2018 17:04:31 -0500 Subject: [PATCH 043/671] Revert "Remove @hide from ViewGroup debug methods" This reverts commit 1244815e8faa6881a9d7256a5a292f88cd7b8108. Bug: 71555179 Test: make Change-Id: Ibfd97660e5f91fc7fac13b66efa8aa587ba20b4e --- api/current.txt | 3 -- core/java/android/view/ViewGroup.java | 35 +++++------------------- core/java/android/widget/GridLayout.java | 6 ++++ 3 files changed, 13 insertions(+), 31 deletions(-) diff --git a/api/current.txt b/api/current.txt index 766c2764823..2581f93b3fe 100644 --- a/api/current.txt +++ b/api/current.txt @@ -48045,8 +48045,6 @@ package android.view { method public void notifySubtreeAccessibilityStateChanged(android.view.View, android.view.View, int); method public final void offsetDescendantRectToMyCoords(android.view.View, android.graphics.Rect); method public final void offsetRectIntoDescendantCoords(android.view.View, android.graphics.Rect); - method protected void onDebugDraw(android.graphics.Canvas); - method protected void onDebugDrawMargins(android.graphics.Canvas, android.graphics.Paint); method public boolean onInterceptHoverEvent(android.view.MotionEvent); method public boolean onInterceptTouchEvent(android.view.MotionEvent); method protected abstract void onLayout(boolean, int, int, int, int); @@ -48120,7 +48118,6 @@ package android.view { ctor public ViewGroup.LayoutParams(android.content.Context, android.util.AttributeSet); ctor public ViewGroup.LayoutParams(int, int); ctor public ViewGroup.LayoutParams(android.view.ViewGroup.LayoutParams); - method public void onDebugDraw(android.view.View, android.graphics.Canvas, android.graphics.Paint); method public void resolveLayoutDirection(int); method protected void setBaseAttributes(android.content.res.TypedArray, int, int); field public static final deprecated int FILL_PARENT = -1; // 0xffffffff diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index a8bdb85660d..9cd0d6309ec 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -3968,15 +3968,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } /** - * Layout debugging code which draws rectangles around layout params. - * - *

This function is called automatically when the developer setting is enabled.

- * - *

It is strongly advised to only call this function from debug builds as there is - * a risk of leaking unwanted layout information.

- * - * @param canvas the canvas on which to draw - * @param paint the paint used to draw through + * @hide */ protected void onDebugDrawMargins(Canvas canvas, Paint paint) { for (int i = 0; i < getChildCount(); i++) { @@ -3986,19 +3978,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } /** - * Layout debugging code which draws rectangles around: - *

    - *
  • optical bounds
  • - *
  • margins
  • - *
  • clip bounds
  • - *
      - * - *

      This function is called automatically when the developer setting is enabled.

      - * - *

      It is strongly advised to only call this function from debug builds as there is - * a risk of leaking unwanted layout information.

      - * - * @param canvas the canvas on which to draw + * @hide */ protected void onDebugDraw(Canvas canvas) { Paint paint = getDebugPaint(); @@ -7715,14 +7695,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager /** * Use {@code canvas} to draw suitable debugging annotations for these LayoutParameters. * - *

      This function is called automatically when the developer setting is enabled.

      - * - *

      It is strongly advised to only call this function from debug builds as there is - * a risk of leaking unwanted layout information.

      - * * @param view the view that contains these layout parameters * @param canvas the canvas on which to draw - * @param paint the paint used to draw through + * + * @hide */ public void onDebugDraw(View view, Canvas canvas, Paint paint) { } @@ -8226,6 +8202,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager return ((mMarginFlags & LAYOUT_DIRECTION_MASK) == View.LAYOUT_DIRECTION_RTL); } + /** + * @hide + */ @Override public void onDebugDraw(View view, Canvas canvas, Paint paint) { Insets oi = isLayoutModeOptical(view.mParent) ? view.getOpticalInsets() : Insets.NONE; diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java index 012b918ff34..3aae8497ba1 100644 --- a/core/java/android/widget/GridLayout.java +++ b/core/java/android/widget/GridLayout.java @@ -904,6 +904,9 @@ public class GridLayout extends ViewGroup { } } + /** + * @hide + */ @Override protected void onDebugDrawMargins(Canvas canvas, Paint paint) { // Apply defaults, so as to remove UNDEFINED values @@ -919,6 +922,9 @@ public class GridLayout extends ViewGroup { } } + /** + * @hide + */ @Override protected void onDebugDraw(Canvas canvas) { Paint paint = new Paint(); -- GitLab From 3410634f2c6454626b1ec4b11f5b6f036a1141e2 Mon Sep 17 00:00:00 2001 From: Tej Singh Date: Fri, 2 Feb 2018 17:08:05 -0800 Subject: [PATCH 044/671] Benchmark: stats_write benchmarks how long stats_write takes for boot sequence atom results: I ran it a few times, and the times ranged from ~18-20us on marlin Test: ran the benchmark test Change-Id: I900ef26ce219301a6d43999fe7be5e4875ae5b8a --- cmds/statsd/Android.mk | 6 ++- .../benchmark/stats_write_benchmark.cpp | 40 +++++++++++++++++++ 2 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 cmds/statsd/benchmark/stats_write_benchmark.cpp diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk index eabbb96a392..d0d05c543d3 100644 --- a/cmds/statsd/Android.mk +++ b/cmds/statsd/Android.mk @@ -232,7 +232,8 @@ LOCAL_MODULE := statsd_benchmark LOCAL_SRC_FILES := $(statsd_common_src) \ benchmark/main.cpp \ benchmark/hello_world_benchmark.cpp \ - benchmark/log_event_benchmark.cpp + benchmark/log_event_benchmark.cpp \ + benchmark/stats_write_benchmark.cpp LOCAL_C_INCLUDES := $(statsd_common_c_includes) @@ -251,7 +252,8 @@ LOCAL_STATIC_LIBRARIES := \ $(statsd_common_static_libraries) LOCAL_SHARED_LIBRARIES := $(statsd_common_shared_libraries) \ - libgtest_prod + libgtest_prod \ + libstatslog LOCAL_PROTOC_OPTIMIZE_TYPE := lite diff --git a/cmds/statsd/benchmark/stats_write_benchmark.cpp b/cmds/statsd/benchmark/stats_write_benchmark.cpp new file mode 100644 index 00000000000..f5a0cd5dfb3 --- /dev/null +++ b/cmds/statsd/benchmark/stats_write_benchmark.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2018 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 "benchmark/benchmark.h" +#include + +namespace android { +namespace os { +namespace statsd { + +static void BM_StatsWrite(benchmark::State& state) { + const char* reason = "test"; + int64_t boot_end_time = 1234567; + int64_t total_duration = 100; + int64_t bootloader_duration = 10; + int64_t time_since_last_boot = 99999999; + while (state.KeepRunning()) { + android::util::stats_write( + android::util::BOOT_SEQUENCE_REPORTED, reason, reason, + boot_end_time, total_duration, bootloader_duration, time_since_last_boot); + total_duration++; + } +} +BENCHMARK(BM_StatsWrite); + +} // namespace statsd +} // namespace os +} // namespace android -- GitLab From 3c71449d1a9147e5f32807e2c17a0369d7603c47 Mon Sep 17 00:00:00 2001 From: Todd Kennedy Date: Fri, 27 Oct 2017 09:12:50 -0700 Subject: [PATCH 045/671] Send package to permission check While splitting the permissions from the package manager service, we adjusted the implementation of checkUidPermission() which queried for package objects separately from obtaining the package names belonging to a UID. This caused some subtle timing issue where the package object sometimes failed to be present. Instead, send the affected package while in a sycnhronized block to avoid the timing issue. Fixes: 68260103 Test: Manual Change-Id: I0b0e124ffd12a31569a1ed0d8a6a7b82cf824d63 --- .../server/pm/PackageManagerService.java | 8 ++++- .../permission/PermissionManagerInternal.java | 3 +- .../permission/PermissionManagerService.java | 29 +++++-------------- 3 files changed, 17 insertions(+), 23 deletions(-) diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index c6b55cc1d55..1360bfcaf7c 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -5216,7 +5216,13 @@ Slog.e("TODD", @Override public int checkUidPermission(String permName, int uid) { - return mPermissionManager.checkUidPermission(permName, uid, getCallingUid()); + synchronized (mPackages) { + final String[] packageNames = getPackagesForUid(uid); + final PackageParser.Package pkg = (packageNames != null && packageNames.length > 0) + ? mPackages.get(packageNames[0]) + : null; + return mPermissionManager.checkUidPermission(permName, pkg, uid, getCallingUid()); + } } @Override diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java index 60c118b5840..859bbe33e64 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java @@ -146,7 +146,8 @@ public abstract class PermissionManagerInternal { public abstract int checkPermission(@NonNull String permName, @NonNull String packageName, int callingUid, int userId); - public abstract int checkUidPermission(String permName, int uid, int callingUid); + public abstract int checkUidPermission(@NonNull String permName, + @Nullable PackageParser.Package pkg, int uid, int callingUid); /** * Enforces the request is from the system or an app that has INTERACT_ACROSS_USERS diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index cb3b1073a59..12aef532921 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -242,7 +242,8 @@ public class PermissionManagerService { return PackageManager.PERMISSION_DENIED; } - private int checkUidPermission(String permName, int uid, int callingUid) { + private int checkUidPermission(String permName, PackageParser.Package pkg, int uid, + int callingUid) { final int callingUserId = UserHandle.getUserId(callingUid); final boolean isCallerInstantApp = mPackageManagerInt.getInstantAppPackageName(callingUid) != null; @@ -253,28 +254,13 @@ public class PermissionManagerService { return PackageManager.PERMISSION_DENIED; } - final String[] packages = mContext.getPackageManager().getPackagesForUid(uid); - if (packages != null && packages.length > 0) { - PackageParser.Package pkg = null; - for (String packageName : packages) { - pkg = mPackageManagerInt.getPackage(packageName); - if (pkg != null) { - break; - } - } - if (pkg == null) { -Slog.e(TAG, "TODD: No package not found; UID: " + uid); -Slog.e(TAG, "TODD: Packages: " + Arrays.toString(packages)); - return PackageManager.PERMISSION_DENIED; - } + if (pkg != null) { if (pkg.mSharedUserId != null) { if (isCallerInstantApp) { return PackageManager.PERMISSION_DENIED; } - } else { - if (mPackageManagerInt.filterAppAccess(pkg, callingUid, callingUserId)) { - return PackageManager.PERMISSION_DENIED; - } + } else if (mPackageManagerInt.filterAppAccess(pkg, callingUid, callingUserId)) { + return PackageManager.PERMISSION_DENIED; } final PermissionsState permissionsState = ((PackageSetting) pkg.mExtras).getPermissionsState(); @@ -2054,8 +2040,9 @@ Slog.e(TAG, "TODD: Packages: " + Arrays.toString(packages)); permName, packageName, callingUid, userId); } @Override - public int checkUidPermission(String permName, int uid, int callingUid) { - return PermissionManagerService.this.checkUidPermission(permName, uid, callingUid); + public int checkUidPermission(String permName, PackageParser.Package pkg, int uid, + int callingUid) { + return PermissionManagerService.this.checkUidPermission(permName, pkg, uid, callingUid); } @Override public PermissionGroupInfo getPermissionGroupInfo(String groupName, int flags, -- GitLab From 9e1af767fa8710ba440ce55d4104c85a2f4256c7 Mon Sep 17 00:00:00 2001 From: Julia Reynolds Date: Mon, 5 Feb 2018 15:06:42 -0500 Subject: [PATCH 046/671] Disable media route scanning Change-Id: Icc308d1df76f0a3f0fd33de9f21c3b0db16cfda0 Fixes: 72951601 Test: runtest systemui --- .../systemui/volume/OutputChooserDialog.java | 5 +++-- .../systemui/volume/OutputChooserDialogTest.java | 14 +++++--------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/volume/OutputChooserDialog.java b/packages/SystemUI/src/com/android/systemui/volume/OutputChooserDialog.java index 5c888ac89c1..46c43c206f8 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/OutputChooserDialog.java +++ b/packages/SystemUI/src/com/android/systemui/volume/OutputChooserDialog.java @@ -81,6 +81,7 @@ public class OutputChooserDialog extends Dialog private final MediaRouterWrapper mRouter; private final MediaRouterCallback mRouterCallback; private long mLastUpdateTime; + static final boolean INCLUDE_MEDIA_ROUTES = false; private boolean mIsInCall; protected boolean isAttached; @@ -174,7 +175,7 @@ public class OutputChooserDialog extends Dialog public void onAttachedToWindow() { super.onAttachedToWindow(); - if (!mIsInCall) { + if (!mIsInCall && INCLUDE_MEDIA_ROUTES) { mRouter.addCallback(mRouteSelector, mRouterCallback, MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN); } @@ -272,7 +273,7 @@ public class OutputChooserDialog extends Dialog addBluetoothDevices(items); // Add remote displays - if (!mIsInCall) { + if (!mIsInCall && INCLUDE_MEDIA_ROUTES) { addRemoteDisplayRoutes(items); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java index f7bb0655b46..922bde787a8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java @@ -16,6 +16,8 @@ package com.android.systemui.volume; +import static com.android.systemui.volume.OutputChooserDialog.INCLUDE_MEDIA_ROUTES; + import static junit.framework.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; @@ -148,20 +150,14 @@ public class OutputChooserDialogTest extends SysuiTestCase { verify(mRouter, never()).addCallback(any(), any(), anyInt()); } - @Test - public void testMediaScanIfNotInCall() { - mDialog.setIsInCall(false); - mDialog.onAttachedToWindow(); - - verify(mRouter, times(1)).addCallback(any(), any(), anyInt()); - } - @Test public void testRegisterCallbacks() { mDialog.setIsInCall(false); mDialog.onAttachedToWindow(); - verify(mRouter, times(1)).addCallback(any(), any(), anyInt()); + if (INCLUDE_MEDIA_ROUTES) { + verify(mRouter, times(1)).addCallback(any(), any(), anyInt()); + } verify(mController, times(1)).addCallback(any()); verify(mVolumeController, times(1)).addCallback(any(), any()); } -- GitLab From 87e485e51da9eeda9dd364baaa189a95c1c65b23 Mon Sep 17 00:00:00 2001 From: Amith Yamasani Date: Mon, 5 Feb 2018 12:14:50 -0800 Subject: [PATCH 047/671] Update Inactive Apps to show standby buckets Update strings to reflect the new values. Bug: 72728900 Test: Settings>Dev>Standby apps Change-Id: Iec55752632efdcce1fefb0dd542858d86eb7d982 --- packages/SettingsLib/res/values/strings.xml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 486a9bbed1f..4d766483321 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -805,13 +805,17 @@ Colors optimized for digital content - - Inactive apps + + Standby apps Inactive. Tap to toggle. Active. Tap to toggle. + + App standby + state: %s + Running services -- GitLab From f9ddcf69b2baff3cdb2272713a167cfb64166640 Mon Sep 17 00:00:00 2001 From: Mengjun Leng Date: Thu, 21 Dec 2017 11:20:58 +0800 Subject: [PATCH 048/671] Overload setTelephonyProperty without phone ID Some properties are not per-phone. Test: ServiceStateTrackerTest.java Fixes: 62048110 Change-Id: I80ffc85b511e6a173a1dee50412b1fc48ef43d90 --- .../android/telephony/TelephonyManager.java | 31 +++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 96ed20b841b..7f0b7025899 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -4563,7 +4563,7 @@ public class TelephonyManager { } /** - * Sets the telephony property with the value specified. + * Sets a per-phone telephony property with the value specified. * * @hide */ @@ -4612,6 +4612,20 @@ public class TelephonyManager { SystemProperties.set(property, propVal); } + /** + * Sets a global telephony property with the value specified. + * + * @hide + */ + public static void setTelephonyProperty(String property, String value) { + if (value == null) { + value = ""; + } + Rlog.d(TAG, "setTelephonyProperty: success" + " property=" + + property + " value: " + value); + SystemProperties.set(property, value); + } + /** * Convenience function for retrieving a value from the secure settings * value list as an integer. Note that internally setting values are @@ -4701,7 +4715,7 @@ public class TelephonyManager { } /** - * Gets the telephony property. + * Gets a per-phone telephony property. * * @hide */ @@ -4717,6 +4731,19 @@ public class TelephonyManager { return propVal == null ? defaultVal : propVal; } + /** + * Gets a global telephony property. + * + * See also getTelephonyProperty(phoneId, property, defaultVal). Most telephony properties are + * per-phone. + * + * @hide + */ + public static String getTelephonyProperty(String property, String defaultVal) { + String propVal = SystemProperties.get(property); + return propVal == null ? defaultVal : propVal; + } + /** @hide */ public int getSimCount() { // FIXME Need to get it from Telephony Dev Controller when that gets implemented! -- GitLab From d65595a94e2e5a5dc19540fcb1ba4ff45a7d2b67 Mon Sep 17 00:00:00 2001 From: Tobias Thierer Date: Mon, 5 Feb 2018 15:49:52 +0000 Subject: [PATCH 049/671] Pin jarjar targets to java_version 1.8. This CL pins the following make targets to java_version 1.8, which is currently the default: framework-protos repackaged.android.test.base repackaged.android.test.mock repackaged.android.test.runner For consistency, their dependencies, android.test.base android.test.mock android.test.runner which contain .java source files, are also pinned to 1.8. This is so that the two steps: a) update jarjar to support v53 class files b) support -target 1.9 in the rest of the toolchain can be completed in any order, in future CLs. Before this CL, they would have needed to be completed in order a), b). Bug: 72703434 Test: EXPERIMENTAL_USE_OPENJDK9=true USE_R8=true make checkbuild docs (in a client where CL http://r.android.com/596874 was reverted) Change-Id: If78067294ae7ab78997aa109b0e08be427bdf0b8 --- proto/Android.bp | 2 ++ test-base/Android.bp | 6 +++++- test-mock/Android.bp | 4 ++++ test-runner/Android.bp | 4 ++++ 4 files changed, 15 insertions(+), 1 deletion(-) diff --git a/proto/Android.bp b/proto/Android.bp index 95f453c3e52..f3811bdd7d8 100644 --- a/proto/Android.bp +++ b/proto/Android.bp @@ -6,6 +6,8 @@ java_library_static { }, srcs: ["src/**/*.proto"], no_framework_libs: true, + // Pin java_version until jarjar is certified to support later versions. http://b/72703434 + java_version: "1.8", target: { android: { jarjar_rules: "jarjar-rules.txt", diff --git a/test-base/Android.bp b/test-base/Android.bp index 62fed61da27..cc7d4bd44bd 100644 --- a/test-base/Android.bp +++ b/test-base/Android.bp @@ -24,6 +24,9 @@ java_library { srcs: ["src/**/*.java"], + // Needs to be consistent with the repackaged version of this make target. + java_version: "1.8", + no_framework_libs: true, hostdex: true, libs: [ @@ -55,13 +58,14 @@ java_library_static { name: "repackaged.android.test.base", static_libs: ["android.test.base"], - no_framework_libs: true, libs: [ "framework", ], jarjar_rules: "jarjar-rules.txt", + // Pin java_version until jarjar is certified to support later versions. http://b/72703434 + java_version: "1.8", } // Build the android.test.base-minus-junit library diff --git a/test-mock/Android.bp b/test-mock/Android.bp index b1ae40e17b9..54e07a1673e 100644 --- a/test-mock/Android.bp +++ b/test-mock/Android.bp @@ -19,6 +19,8 @@ java_library { name: "android.test.mock", + // Needs to be consistent with the repackaged version of this make target. + java_version: "1.8", srcs: ["src/**/*.java"], no_framework_libs: true, @@ -35,4 +37,6 @@ java_library_static { static_libs: ["android.test.mock"], jarjar_rules: "jarjar-rules.txt", + // Pin java_version until jarjar is certified to support later versions. http://b/72703434 + java_version: "1.8", } diff --git a/test-runner/Android.bp b/test-runner/Android.bp index dfaeed5e271..b902011dc99 100644 --- a/test-runner/Android.bp +++ b/test-runner/Android.bp @@ -19,6 +19,8 @@ java_library { name: "android.test.runner", + // Needs to be consistent with the repackaged version of this make target. + java_version: "1.8", srcs: ["src/**/*.java"], no_framework_libs: true, @@ -54,4 +56,6 @@ java_library_static { static_libs: ["android.test.runner"], jarjar_rules: "jarjar-rules.txt", + // Pin java_version until jarjar is certified to support later versions. http://b/72703434 + java_version: "1.8", } -- GitLab From 622b9f921278b308e9497675e63159f926764c91 Mon Sep 17 00:00:00 2001 From: Vladislav Kuzkokov Date: Thu, 25 Jan 2018 16:33:05 +0100 Subject: [PATCH 050/671] Make printing policy a restriction. Use existing API instead of creating new method. Bug: 64140119 Test: cts-tradefed run cts-dev --module CtsDevicePolicyManagerTestCases --test com.android.cts.devicepolicy.MixedDeviceOwnerTest#testPrintingPolicy Change-Id: I9ff94f4d73824e7bf9aedbb64811ad60fccf9779 --- api/current.txt | 3 +- .../app/admin/DevicePolicyManager.java | 35 ------------ .../app/admin/IDevicePolicyManager.aidl | 3 - core/java/android/os/UserManager.java | 14 +++++ .../server/pm/UserRestrictionsUtils.java | 3 +- .../BaseIDevicePolicyManager.java | 10 ---- .../DevicePolicyManagerService.java | 55 +------------------ .../server/print/PrintManagerService.java | 6 +- 8 files changed, 19 insertions(+), 110 deletions(-) diff --git a/api/current.txt b/api/current.txt index 1ffba87773a..09260af23b9 100644 --- a/api/current.txt +++ b/api/current.txt @@ -6493,7 +6493,6 @@ package android.app.admin { method public boolean isNetworkLoggingEnabled(android.content.ComponentName); method public boolean isOverrideApnEnabled(android.content.ComponentName); method public boolean isPackageSuspended(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException; - method public boolean isPrintingEnabled(); method public boolean isProfileOwnerApp(java.lang.String); method public boolean isProvisioningAllowed(java.lang.String); method public boolean isResetPasswordTokenActive(android.content.ComponentName); @@ -6566,7 +6565,6 @@ package android.app.admin { method public boolean setPermittedAccessibilityServices(android.content.ComponentName, java.util.List); method public boolean setPermittedCrossProfileNotificationListeners(android.content.ComponentName, java.util.List); method public boolean setPermittedInputMethods(android.content.ComponentName, java.util.List); - method public void setPrintingEnabled(android.content.ComponentName, boolean); method public void setProfileEnabled(android.content.ComponentName); method public void setProfileName(android.content.ComponentName, java.lang.String); method public void setRecommendedGlobalProxy(android.content.ComponentName, android.net.ProxyInfo); @@ -33140,6 +33138,7 @@ package android.os { field public static final java.lang.String DISALLOW_NETWORK_RESET = "no_network_reset"; field public static final java.lang.String DISALLOW_OUTGOING_BEAM = "no_outgoing_beam"; field public static final java.lang.String DISALLOW_OUTGOING_CALLS = "no_outgoing_calls"; + field public static final java.lang.String DISALLOW_PRINTING = "no_printing"; field public static final java.lang.String DISALLOW_REMOVE_MANAGED_PROFILE = "no_remove_managed_profile"; field public static final java.lang.String DISALLOW_REMOVE_USER = "no_remove_user"; field public static final java.lang.String DISALLOW_SAFE_BOOT = "no_safe_boot"; diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 8d161006be7..77e118ccb1b 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -9403,41 +9403,6 @@ public class DevicePolicyManager { } } - /** - * Allows/disallows printing. - * - * Called by a device owner or a profile owner. - * Device owner changes policy for all users. Profile owner can override it if present. - * Printing is enabled by default. If {@code FEATURE_PRINTING} is absent, the call is ignored. - * - * @param admin which {@link DeviceAdminReceiver} this request is associated with. - * @param enabled whether printing should be allowed or not. - * @throws SecurityException if {@code admin} is neither device, nor profile owner. - */ - public void setPrintingEnabled(@NonNull ComponentName admin, boolean enabled) { - try { - mService.setPrintingEnabled(admin, enabled); - } catch (RemoteException re) { - throw re.rethrowFromSystemServer(); - } - } - - /** - * Returns whether printing is enabled for this user. - * - * Always {@code false} if {@code FEATURE_PRINTING} is absent. - * Otherwise, {@code true} by default. - * - * @return {@code true} iff printing is enabled. - */ - public boolean isPrintingEnabled() { - try { - return mService.isPrintingEnabled(); - } catch (RemoteException re) { - throw re.rethrowFromSystemServer(); - } - } - /** * Called by device owner to add an override APN. * diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index ef990071dbf..5218a7340ec 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -402,9 +402,6 @@ interface IDevicePolicyManager { CharSequence getStartUserSessionMessage(in ComponentName admin); CharSequence getEndUserSessionMessage(in ComponentName admin); - void setPrintingEnabled(in ComponentName admin, boolean enabled); - boolean isPrintingEnabled(); - List setMeteredDataDisabled(in ComponentName admin, in List packageNames); List getMeteredDataDisabled(in ComponentName admin); diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 2093cec769d..7e7af1a1229 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -943,6 +943,20 @@ public class UserManager { * @see #getUserRestrictions() */ public static final String DISALLOW_SHARE_INTO_MANAGED_PROFILE = "no_sharing_into_profile"; + + /** + * Specifies whether the user is allowed to print. + * + * This restriction can be set by device or profile owner. + * + * The default value is {@code false}. + * + * @see DevicePolicyManager#addUserRestriction(ComponentName, String) + * @see DevicePolicyManager#clearUserRestriction(ComponentName, String) + * @see #getUserRestrictions() + */ + public static final String DISALLOW_PRINTING = "no_printing"; + /** * Application restriction key that is used to indicate the pending arrival * of real restrictions for the app. diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java index 842f8d0a42f..23185d7fb5a 100644 --- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java +++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java @@ -122,7 +122,8 @@ public class UserRestrictionsUtils { UserManager.DISALLOW_CONFIG_BRIGHTNESS, UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, UserManager.DISALLOW_AMBIENT_DISPLAY, - UserManager.DISALLOW_CONFIG_SCREEN_TIMEOUT + UserManager.DISALLOW_CONFIG_SCREEN_TIMEOUT, + UserManager.DISALLOW_PRINTING }); /** diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java index c4e485c1523..3557dc90a50 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java @@ -121,16 +121,6 @@ abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub { return null; } - @Override - public void setPrintingEnabled(ComponentName admin, boolean enabled) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isPrintingEnabled() { - return true; - } - @Override public List setMeteredDataDisabled(ComponentName admin, List packageNames) { return packageNames; diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 4afe1c78470..953a79f625e 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -305,8 +305,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { private static final String TAG_PASSWORD_VALIDITY = "password-validity"; - private static final String TAG_PRINTING_ENABLED = "printing-enabled"; - private static final String TAG_TRANSFER_OWNERSHIP_BUNDLE = "transfer-ownership-bundle"; private static final int REQUEST_EXPIRE_PASSWORD = 5571; @@ -615,8 +613,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { long mPasswordTokenHandle = 0; - boolean mPrintingEnabled = true; - public DevicePolicyData(int userHandle) { mUserHandle = userHandle; } @@ -2948,12 +2944,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { out.endTag(null, TAG_CURRENT_INPUT_METHOD_SET); } - if (!policy.mPrintingEnabled) { - out.startTag(null, TAG_PRINTING_ENABLED); - out.attribute(null, ATTR_VALUE, Boolean.toString(policy.mPrintingEnabled)); - out.endTag(null, TAG_PRINTING_ENABLED); - } - for (final String cert : policy.mOwnerInstalledCaCerts) { out.startTag(null, TAG_OWNER_INSTALLED_CA_CERT); out.attribute(null, ATTR_ALIAS, cert); @@ -3172,9 +3162,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { policy.mCurrentInputMethodSet = true; } else if (TAG_OWNER_INSTALLED_CA_CERT.equals(tag)) { policy.mOwnerInstalledCaCerts.add(parser.getAttributeValue(null, ATTR_ALIAS)); - } else if (TAG_PRINTING_ENABLED.equals(tag)) { - String enabled = parser.getAttributeValue(null, ATTR_VALUE); - policy.mPrintingEnabled = Boolean.toString(true).equals(enabled); } else { Slog.w(LOG_TAG, "Unknown tag: " + tag); XmlUtils.skipCurrentTag(parser); @@ -10417,7 +10404,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public CharSequence getPrintingDisabledReasonForUser(@UserIdInt int userId) { synchronized (DevicePolicyManagerService.this) { DevicePolicyData policy = getUserData(userId); - if (policy.mPrintingEnabled) { + if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_PRINTING)) { Log.e(LOG_TAG, "printing is enabled"); return null; } @@ -12789,27 +12776,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } - private boolean hasPrinting() { - return mInjector.getPackageManager().hasSystemFeature(PackageManager.FEATURE_PRINTING); - } - - @Override - public void setPrintingEnabled(ComponentName admin, boolean enabled) { - if (!mHasFeature || !hasPrinting()) { - return; - } - Preconditions.checkNotNull(admin, "Admin cannot be null."); - enforceProfileOrDeviceOwner(admin); - synchronized (this) { - final int userHandle = mInjector.userHandleGetCallingUserId(); - DevicePolicyData policy = getUserData(userHandle); - if (policy.mPrintingEnabled != enabled) { - policy.mPrintingEnabled = enabled; - saveSettingsLocked(userHandle); - } - } - } - private void deleteTransferOwnershipMetadataFileLocked() { mTransferOwnershipMetadataManager.deleteMetadataFile(); } @@ -12839,25 +12805,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } - /** - * Returns whether printing is enabled for current user. - * @hide - */ - @Override - public boolean isPrintingEnabled() { - if (!hasPrinting()) { - return false; - } - if (!mHasFeature) { - return true; - } - synchronized (this) { - final int userHandle = mInjector.userHandleGetCallingUserId(); - DevicePolicyData policy = getUserData(userHandle); - return policy.mPrintingEnabled; - } - } - @Override public int addOverrideApn(@NonNull ComponentName who, @NonNull ApnSetting apnSetting) { if (!mHasFeature) { diff --git a/services/print/java/com/android/server/print/PrintManagerService.java b/services/print/java/com/android/server/print/PrintManagerService.java index e1c1eb298e3..e8620edc9d5 100644 --- a/services/print/java/com/android/server/print/PrintManagerService.java +++ b/services/print/java/com/android/server/print/PrintManagerService.java @@ -21,7 +21,6 @@ import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING; import android.annotation.NonNull; import android.app.ActivityManager; -import android.app.admin.DevicePolicyManager; import android.app.admin.DevicePolicyManagerInternal; import android.content.ComponentName; import android.content.Context; @@ -115,12 +114,9 @@ public final class PrintManagerService extends SystemService { private final SparseArray mUserStates = new SparseArray<>(); - private final DevicePolicyManager mDpm; - PrintManagerImpl(Context context) { mContext = context; mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); - mDpm = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE); registerContentObservers(); registerBroadcastReceivers(); } @@ -722,7 +718,7 @@ public final class PrintManagerService extends SystemService { } private boolean isPrintingEnabled() { - return mDpm == null || mDpm.isPrintingEnabled(); + return !mUserManager.hasUserRestriction(UserManager.DISALLOW_PRINTING); } private void dump(@NonNull DualDumpOutputStream dumpStream, -- GitLab From 789289d7311b80d4e048502a1f33ab9fc8da39f1 Mon Sep 17 00:00:00 2001 From: Malcolm Chen Date: Mon, 29 Jan 2018 15:10:46 -0800 Subject: [PATCH 051/671] Add strings and carrier config needed network service. Add resource overlay and carrier config which will determine which package / network services will be bound to. Bug: 64132030 Test: regression tests Change-Id: I5f515ec16b712e7be25f69e0e079d672227542b0 Merged-In: I5f515ec16b712e7be25f69e0e079d672227542b0 --- api/system-current.txt | 1 + core/res/AndroidManifest.xml | 9 +++++++++ core/res/res/values/config.xml | 7 +++++++ core/res/res/values/symbols.xml | 2 ++ .../telephony/CarrierConfigManager.java | 18 ++++++++++++++++++ .../java/android/telephony/NetworkService.java | 10 ++++++++-- 6 files changed, 45 insertions(+), 2 deletions(-) diff --git a/api/system-current.txt b/api/system-current.txt index 97e82646281..d8a84a94e70 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -29,6 +29,7 @@ package android { field public static final java.lang.String BIND_RESOLVER_RANKER_SERVICE = "android.permission.BIND_RESOLVER_RANKER_SERVICE"; field public static final java.lang.String BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE = "android.permission.BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE"; field public static final java.lang.String BIND_TELEPHONY_DATA_SERVICE = "android.permission.BIND_TELEPHONY_DATA_SERVICE"; + field public static final java.lang.String BIND_TELEPHONY_NETWORK_SERVICE = "android.permission.BIND_TELEPHONY_NETWORK_SERVICE"; field public static final java.lang.String BIND_TRUST_AGENT = "android.permission.BIND_TRUST_AGENT"; field public static final java.lang.String BIND_TV_REMOTE_SERVICE = "android.permission.BIND_TV_REMOTE_SERVICE"; field public static final java.lang.String BLUETOOTH_PRIVILEGED = "android.permission.BLUETOOTH_PRIVILEGED"; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index ada9bd76935..b165ab122ba 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1765,6 +1765,15 @@ + + + + com.android.phone + + + diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 08be23d94a5..0aec47e66aa 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -266,6 +266,8 @@ + + diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 78a0ec27623..046b8be90a7 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -1800,6 +1800,22 @@ public class CarrierConfigManager { public static final String KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY = "lte_rsrp_thresholds_int_array"; + /** + * Decides when clients try to bind to iwlan network service, which package name will + * the binding intent go to. + * @hide + */ + public static final String KEY_CARRIER_NETWORK_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING + = "carrier_network_service_wlan_package_override_string"; + + /** + * Decides when clients try to bind to wwan (cellular) network service, which package name will + * the binding intent go to. + * @hide + */ + public static final String KEY_CARRIER_NETWORK_SERVICE_WWAN_PACKAGE_OVERRIDE_STRING + = "carrier_network_service_wwan_package_override_string"; + /** The default value for every variable. */ private final static PersistableBundle sDefaults; @@ -1839,6 +1855,8 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL, true); sDefaults.putString(KEY_CARRIER_DATA_SERVICE_WWAN_PACKAGE_OVERRIDE_STRING, ""); sDefaults.putString(KEY_CARRIER_DATA_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING, ""); + sDefaults.putString(KEY_CARRIER_NETWORK_SERVICE_WWAN_PACKAGE_OVERRIDE_STRING, ""); + sDefaults.putString(KEY_CARRIER_NETWORK_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING, ""); sDefaults.putString(KEY_CARRIER_INSTANT_LETTERING_INVALID_CHARS_STRING, ""); sDefaults.putString(KEY_CARRIER_INSTANT_LETTERING_ESCAPED_CHARS_STRING, ""); sDefaults.putString(KEY_CARRIER_INSTANT_LETTERING_ENCODING_STRING, ""); diff --git a/telephony/java/android/telephony/NetworkService.java b/telephony/java/android/telephony/NetworkService.java index 94921de6829..35682a74744 100644 --- a/telephony/java/android/telephony/NetworkService.java +++ b/telephony/java/android/telephony/NetworkService.java @@ -28,6 +28,8 @@ import android.os.Message; import android.os.RemoteException; import android.util.SparseArray; +import com.android.internal.annotations.VisibleForTesting; + import java.util.ArrayList; import java.util.List; @@ -38,7 +40,7 @@ import java.util.List; * follow the following format: * ... * + * android:permission="android.permission.BIND_TELEPHONY_NETWORK_SERVICE" > * * * @@ -68,7 +70,11 @@ public abstract class NetworkService extends Service { private final SparseArray mServiceMap = new SparseArray<>(); - private final INetworkServiceWrapper mBinder = new INetworkServiceWrapper(); + /** + * @hide + */ + @VisibleForTesting + public final INetworkServiceWrapper mBinder = new INetworkServiceWrapper(); /** * The abstract class of the actual network service implementation. The network service provider -- GitLab From e975a4242ad5f76afc1674512c80755c5fccc73c Mon Sep 17 00:00:00 2001 From: Beverly Date: Mon, 5 Feb 2018 16:56:02 -0500 Subject: [PATCH 052/671] Adding warning text to dnd dialog Test: make ROBOTEST_FILTER=EnableZenModeDialogTest RunSettingsLibRoboTests -j40 Bug: 72494029 Change-Id: I581f5da71616573b9b76176fdfc3d5cbbfa47005 --- .../zen_mode_turn_on_dialog_container.xml | 53 ++++--- packages/SettingsLib/res/values/strings.xml | 8 + .../notification/EnableZenModeDialog.java | 112 ++++++++++--- .../notification/EnableZenModeDialogTest.java | 149 ++++++++++++++++++ 4 files changed, 280 insertions(+), 42 deletions(-) create mode 100644 packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/EnableZenModeDialogTest.java diff --git a/packages/SettingsLib/res/layout/zen_mode_turn_on_dialog_container.xml b/packages/SettingsLib/res/layout/zen_mode_turn_on_dialog_container.xml index ac56a2d9d2c..bc330c7356b 100644 --- a/packages/SettingsLib/res/layout/zen_mode_turn_on_dialog_container.xml +++ b/packages/SettingsLib/res/layout/zen_mode_turn_on_dialog_container.xml @@ -22,25 +22,40 @@ android:fillViewport ="true" android:orientation="vertical"> - - - - + android:layout_height="match_parent" + android:orientation="vertical"> + + + + + + + + \ No newline at end of file diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 486a9bbed1f..6ef3facc1ae 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -1039,5 +1039,13 @@ Priority only %1$s. %2$s + + You won\'t hear your next alarm %1$s unless you turn this off before then + + You won\'t hear your next alarm %1$s + + at %1$s + + on %1$s diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java b/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java index a20f68753a7..1a54d6a3396 100644 --- a/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java +++ b/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java @@ -1,5 +1,3 @@ -package com.android.settingslib.notification; - /* * Copyright (C) 2018 The Android Open Source Project * @@ -16,6 +14,8 @@ package com.android.settingslib.notification; * limitations under the License. */ +package com.android.settingslib.notification; + import android.app.ActivityManager; import android.app.AlarmManager; import android.app.AlertDialog; @@ -28,6 +28,7 @@ import android.provider.Settings; import android.service.notification.Condition; import android.service.notification.ZenModeConfig; import android.text.TextUtils; +import android.text.format.DateFormat; import android.util.Log; import android.util.Slog; import android.view.LayoutInflater; @@ -40,6 +41,7 @@ import android.widget.RadioGroup; import android.widget.ScrollView; import android.widget.TextView; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; import com.android.internal.policy.PhoneWindow; @@ -48,11 +50,11 @@ import com.android.settingslib.R; import java.util.Arrays; import java.util.Calendar; import java.util.GregorianCalendar; +import java.util.Locale; import java.util.Objects; public class EnableZenModeDialog { - - private static final String TAG = "QSEnableZenModeDialog"; + private static final String TAG = "EnableZenModeDialog"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private static final int[] MINUTE_BUCKETS = ZenModeConfig.MINUTE_BUCKETS; @@ -60,25 +62,37 @@ public class EnableZenModeDialog { private static final int MAX_BUCKET_MINUTES = MINUTE_BUCKETS[MINUTE_BUCKETS.length - 1]; private static final int DEFAULT_BUCKET_INDEX = Arrays.binarySearch(MINUTE_BUCKETS, 60); - private static final int FOREVER_CONDITION_INDEX = 0; - private static final int COUNTDOWN_CONDITION_INDEX = 1; - private static final int COUNTDOWN_ALARM_CONDITION_INDEX = 2; + @VisibleForTesting + protected static final int FOREVER_CONDITION_INDEX = 0; + @VisibleForTesting + protected static final int COUNTDOWN_CONDITION_INDEX = 1; + @VisibleForTesting + protected static final int COUNTDOWN_ALARM_CONDITION_INDEX = 2; private static final int SECONDS_MS = 1000; private static final int MINUTES_MS = 60 * SECONDS_MS; - private Uri mForeverId; + @VisibleForTesting + protected Uri mForeverId; private int mBucketIndex = -1; private AlarmManager mAlarmManager; private int mUserId; private boolean mAttached; - private Context mContext; + @VisibleForTesting + protected Context mContext; + @VisibleForTesting + protected TextView mZenAlarmWarning; + @VisibleForTesting + protected LinearLayout mZenRadioGroupContent; + private RadioGroup mZenRadioGroup; - private LinearLayout mZenRadioGroupContent; private int MAX_MANUAL_DND_OPTIONS = 3; + @VisibleForTesting + protected LayoutInflater mLayoutInflater; + public EnableZenModeDialog(Context context) { mContext = context; } @@ -133,32 +147,40 @@ public class EnableZenModeDialog { for (int i = 0; i < N; i++) { mZenRadioGroupContent.getChildAt(i).setVisibility(View.GONE); } + + mZenAlarmWarning.setVisibility(View.GONE); } protected View getContentView() { - final LayoutInflater inflater = new PhoneWindow(mContext).getLayoutInflater(); - View contentView = inflater.inflate(R.layout.zen_mode_turn_on_dialog_container, null); + if (mLayoutInflater == null) { + mLayoutInflater = new PhoneWindow(mContext).getLayoutInflater(); + } + View contentView = mLayoutInflater.inflate(R.layout.zen_mode_turn_on_dialog_container, + null); ScrollView container = (ScrollView) contentView.findViewById(R.id.container); mZenRadioGroup = container.findViewById(R.id.zen_radio_buttons); mZenRadioGroupContent = container.findViewById(R.id.zen_radio_buttons_content); + mZenAlarmWarning = container.findViewById(R.id.zen_alarm_warning); for (int i = 0; i < MAX_MANUAL_DND_OPTIONS; i++) { - final View radioButton = inflater.inflate(R.layout.zen_mode_radio_button, + final View radioButton = mLayoutInflater.inflate(R.layout.zen_mode_radio_button, mZenRadioGroup, false); mZenRadioGroup.addView(radioButton); radioButton.setId(i); - final View radioButtonContent = inflater.inflate(R.layout.zen_mode_condition, + final View radioButtonContent = mLayoutInflater.inflate(R.layout.zen_mode_condition, mZenRadioGroupContent, false); radioButtonContent.setId(i + MAX_MANUAL_DND_OPTIONS); mZenRadioGroupContent.addView(radioButtonContent); } + hideAllConditions(); return contentView; } - private void bind(final Condition condition, final View row, final int rowId) { + @VisibleForTesting + protected void bind(final Condition condition, final View row, final int rowId) { if (condition == null) throw new IllegalArgumentException("condition must not be null"); final boolean enabled = condition.state == Condition.STATE_TRUE; final ConditionTag tag = row.getTag() != null ? (ConditionTag) row.getTag() : @@ -181,6 +203,7 @@ public class EnableZenModeDialog { if (DEBUG) Log.d(TAG, "onCheckedChanged " + conditionId); MetricsLogger.action(mContext, MetricsProto.MetricsEvent.QS_DND_CONDITION_SELECT); + updateAlarmWarningText(tag.condition); announceConditionSelection(tag); } } @@ -190,11 +213,13 @@ public class EnableZenModeDialog { row.setVisibility(View.VISIBLE); } - private ConditionTag getConditionTagAt(int index) { + @VisibleForTesting + protected ConditionTag getConditionTagAt(int index) { return (ConditionTag) mZenRadioGroupContent.getChildAt(index).getTag(); } - private void bindConditions(Condition c) { + @VisibleForTesting + protected void bindConditions(Condition c) { // forever bind(forever(), mZenRadioGroupContent.getChildAt(FOREVER_CONDITION_INDEX), FOREVER_CONDITION_INDEX); @@ -236,11 +261,13 @@ public class EnableZenModeDialog { return info != null ? info.getTriggerTime() : 0; } - private boolean isAlarm(Condition c) { + @VisibleForTesting + protected boolean isAlarm(Condition c) { return c != null && ZenModeConfig.isValidCountdownToAlarmConditionId(c.id); } - private boolean isCountdown(Condition c) { + @VisibleForTesting + protected boolean isCountdown(Condition c) { return c != null && ZenModeConfig.isValidCountdownConditionId(c.id); } @@ -264,7 +291,8 @@ public class EnableZenModeDialog { } // Returns a time condition if the next alarm is within the next week. - private Condition getTimeUntilNextAlarmCondition() { + @VisibleForTesting + protected Condition getTimeUntilNextAlarmCondition() { GregorianCalendar weekRange = new GregorianCalendar(); setToMidnight(weekRange); weekRange.add(Calendar.DATE, 6); @@ -282,7 +310,8 @@ public class EnableZenModeDialog { return null; } - private void bindGenericCountdown() { + @VisibleForTesting + protected void bindGenericCountdown() { mBucketIndex = DEFAULT_BUCKET_INDEX; Condition countdown = ZenModeConfig.toTimeCondition(mContext, MINUTE_BUCKETS[mBucketIndex], ActivityManager.getCurrentUser()); @@ -366,7 +395,8 @@ public class EnableZenModeDialog { } } - private void bindNextAlarm(Condition c) { + @VisibleForTesting + protected void bindNextAlarm(Condition c) { View alarmContent = mZenRadioGroupContent.getChildAt(COUNTDOWN_ALARM_CONDITION_INDEX); ConditionTag tag = (ConditionTag) alarmContent.getTag(); @@ -415,6 +445,7 @@ public class EnableZenModeDialog { MINUTE_BUCKETS[mBucketIndex], ActivityManager.getCurrentUser()); } bind(newCondition, row, rowId); + updateAlarmWarningText(tag.condition); tag.rb.setChecked(true); announceConditionSelection(tag); } @@ -428,8 +459,43 @@ public class EnableZenModeDialog { } } + private void updateAlarmWarningText(Condition condition) { + String warningText = computeAlarmWarningText(condition); + mZenAlarmWarning.setText(warningText); + mZenAlarmWarning.setVisibility(warningText == null ? View.GONE : View.VISIBLE); + } + + private String computeAlarmWarningText(Condition condition) { + final long now = System.currentTimeMillis(); + final long nextAlarm = getNextAlarm(); + if (nextAlarm < now) { + return null; + } + int warningRes = 0; + if (condition == null || isForever(condition)) { + warningRes = R.string.zen_alarm_warning_indef; + } else { + final long time = ZenModeConfig.tryParseCountdownConditionId(condition.id); + if (time > now && nextAlarm < time) { + warningRes = R.string.zen_alarm_warning; + } + } + if (warningRes == 0) { + return null; + } + final boolean soon = (nextAlarm - now) < 24 * 60 * 60 * 1000; + final boolean is24 = DateFormat.is24HourFormat(mContext, ActivityManager.getCurrentUser()); + final String skeleton = soon ? (is24 ? "Hm" : "hma") : (is24 ? "EEEHm" : "EEEhma"); + final String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton); + final CharSequence formattedTime = DateFormat.format(pattern, nextAlarm); + final int templateRes = soon ? R.string.alarm_template : R.string.alarm_template_far; + final String template = mContext.getResources().getString(templateRes, formattedTime); + return mContext.getResources().getString(warningRes, template); + } + // used as the view tag on condition rows - private static class ConditionTag { + @VisibleForTesting + protected static class ConditionTag { public RadioButton rb; public View lines; public TextView line1; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/EnableZenModeDialogTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/EnableZenModeDialogTest.java new file mode 100644 index 00000000000..777cd98e2ef --- /dev/null +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/EnableZenModeDialogTest.java @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2018 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.notification; + +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +import android.app.Activity; +import android.app.Fragment; +import android.content.Context; +import android.net.Uri; +import android.service.notification.Condition; +import android.view.LayoutInflater; + +import com.android.settingslib.TestConfig; +import com.android.settingslib.SettingsLibRobolectricTestRunner; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +@RunWith(SettingsLibRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class EnableZenModeDialogTest { + private EnableZenModeDialog mController; + + @Mock + private Context mContext; + @Mock + private Fragment mFragment; + + private Context mShadowContext; + private LayoutInflater mLayoutInflater; + private Condition mCountdownCondition; + private Condition mAlarmCondition; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + mShadowContext = RuntimeEnvironment.application; + when(mContext.getApplicationContext()).thenReturn(mContext); + when(mFragment.getContext()).thenReturn(mShadowContext); + mLayoutInflater = LayoutInflater.from(mShadowContext); + + mController = spy(new EnableZenModeDialog(mContext)); + mController.mContext = mContext; + mController.mLayoutInflater = mLayoutInflater; + mController.mForeverId = Condition.newId(mContext).appendPath("forever").build(); + when(mContext.getString(com.android.internal.R.string.zen_mode_forever)) + .thenReturn("testSummary"); + mController.getContentView(); + + // these methods use static calls to ZenModeConfig which would normally fail in robotests, + // so instead do nothing: + doNothing().when(mController).bindGenericCountdown(); + doReturn(null).when(mController).getTimeUntilNextAlarmCondition(); + doReturn(0L).when(mController).getNextAlarm(); + doNothing().when(mController).bindNextAlarm(any()); + + // as a result of doing nothing above, must bind manually: + Uri alarm = Condition.newId(mContext).appendPath("alarm").build(); + mAlarmCondition = new Condition(alarm, "alarm", "", "", 0, 0, 0); + Uri countdown = Condition.newId(mContext).appendPath("countdown").build(); + mCountdownCondition = new Condition(countdown, "countdown", "", "", 0, 0, 0); + mController.bind(mCountdownCondition, + mController.mZenRadioGroupContent.getChildAt( + EnableZenModeDialog.COUNTDOWN_CONDITION_INDEX), + EnableZenModeDialog.COUNTDOWN_CONDITION_INDEX); + mController.bind(mAlarmCondition, + mController.mZenRadioGroupContent.getChildAt( + EnableZenModeDialog.COUNTDOWN_ALARM_CONDITION_INDEX), + EnableZenModeDialog.COUNTDOWN_ALARM_CONDITION_INDEX); + } + + @Test + public void testForeverChecked() { + mController.bindConditions(mController.forever()); + + assertTrue(mController.getConditionTagAt(EnableZenModeDialog.FOREVER_CONDITION_INDEX).rb + .isChecked()); + assertFalse(mController.getConditionTagAt(EnableZenModeDialog.COUNTDOWN_CONDITION_INDEX).rb + .isChecked()); + assertFalse(mController.getConditionTagAt( + EnableZenModeDialog.COUNTDOWN_ALARM_CONDITION_INDEX).rb.isChecked()); + } + + @Test + public void testNoneChecked() { + mController.bindConditions(null); + assertFalse(mController.getConditionTagAt(EnableZenModeDialog.FOREVER_CONDITION_INDEX).rb + .isChecked()); + assertFalse(mController.getConditionTagAt(EnableZenModeDialog.COUNTDOWN_CONDITION_INDEX).rb + .isChecked()); + assertFalse(mController.getConditionTagAt( + EnableZenModeDialog.COUNTDOWN_ALARM_CONDITION_INDEX).rb.isChecked()); + } + + @Test + public void testAlarmChecked() { + doReturn(false).when(mController).isCountdown(mAlarmCondition); + doReturn(true).when(mController).isAlarm(mAlarmCondition); + + mController.bindConditions(mAlarmCondition); + assertFalse(mController.getConditionTagAt(EnableZenModeDialog.FOREVER_CONDITION_INDEX).rb + .isChecked()); + assertFalse(mController.getConditionTagAt(EnableZenModeDialog.COUNTDOWN_CONDITION_INDEX).rb + .isChecked()); + assertTrue(mController.getConditionTagAt( + EnableZenModeDialog.COUNTDOWN_ALARM_CONDITION_INDEX).rb.isChecked()); + } + + @Test + public void testCountdownChecked() { + doReturn(false).when(mController).isAlarm(mCountdownCondition); + doReturn(true).when(mController).isCountdown(mCountdownCondition); + + mController.bindConditions(mCountdownCondition); + assertFalse(mController.getConditionTagAt(EnableZenModeDialog.FOREVER_CONDITION_INDEX).rb + .isChecked()); + assertTrue(mController.getConditionTagAt(EnableZenModeDialog.COUNTDOWN_CONDITION_INDEX).rb + .isChecked()); + assertFalse(mController.getConditionTagAt( + EnableZenModeDialog.COUNTDOWN_ALARM_CONDITION_INDEX).rb.isChecked()); + } +} \ No newline at end of file -- GitLab From 0a01c6b2e143885baaa99174c04a6cefa1294514 Mon Sep 17 00:00:00 2001 From: Malcolm Chen Date: Mon, 29 Jan 2018 17:09:21 -0800 Subject: [PATCH 053/671] Update ServiceState to adapt NetworkService change. SST is re-routed to get cellular registration states from CellularNetworkService. Updating ServiceState to adapt that change. Bug: 64132030 Test: unittest Change-Id: Ifd557ce50a4419ead6125cda29c79d331508448e Merged-In: Ifd557ce50a4419ead6125cda29c79d331508448e --- .../telephony/NetworkRegistrationState.java | 16 ++- .../java/android/telephony/ServiceState.java | 102 ++++++++---------- 2 files changed, 58 insertions(+), 60 deletions(-) diff --git a/telephony/java/android/telephony/NetworkRegistrationState.java b/telephony/java/android/telephony/NetworkRegistrationState.java index 4f137bea69f..bba779d0c17 100644 --- a/telephony/java/android/telephony/NetworkRegistrationState.java +++ b/telephony/java/android/telephony/NetworkRegistrationState.java @@ -298,9 +298,9 @@ public class NetworkRegistrationState implements Parcelable { && mEmergencyOnly == other.mEmergencyOnly && (mAvailableServices == other.mAvailableServices || Arrays.equals(mAvailableServices, other.mAvailableServices)) - && mCellIdentity == other.mCellIdentity - && mVoiceSpecificStates == other.mVoiceSpecificStates - && mDataSpecificStates == other.mDataSpecificStates; + && equals(mCellIdentity, other.mCellIdentity) + && equals(mVoiceSpecificStates, other.mVoiceSpecificStates) + && equals(mDataSpecificStates, other.mDataSpecificStates); } @Override @@ -329,4 +329,14 @@ public class NetworkRegistrationState implements Parcelable { return new NetworkRegistrationState[size]; } }; + + private static boolean equals(Object o1, Object o2) { + if (o1 == o2) { + return true; + } else if (o1 == null) { + return false; + } else { + return o1.equals(o2); + } + } } diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java index 1176491907c..cb867abb74d 100644 --- a/telephony/java/android/telephony/ServiceState.java +++ b/telephony/java/android/telephony/ServiceState.java @@ -94,47 +94,6 @@ public class ServiceState implements Parcelable { */ public static final int DUPLEX_MODE_TDD = 2; - /** - * RIL level registration state values from ril.h - * ((const char **)response)[0] is registration state 0-6, - * 0 - Not registered, MT is not currently searching - * a new operator to register - * 1 - Registered, home network - * 2 - Not registered, but MT is currently searching - * a new operator to register - * 3 - Registration denied - * 4 - Unknown - * 5 - Registered, roaming - * 10 - Same as 0, but indicates that emergency calls - * are enabled. - * 12 - Same as 2, but indicates that emergency calls - * are enabled. - * 13 - Same as 3, but indicates that emergency calls - * are enabled. - * 14 - Same as 4, but indicates that emergency calls - * are enabled. - * @hide - */ - public static final int RIL_REG_STATE_NOT_REG = 0; - /** @hide */ - public static final int RIL_REG_STATE_HOME = 1; - /** @hide */ - public static final int RIL_REG_STATE_SEARCHING = 2; - /** @hide */ - public static final int RIL_REG_STATE_DENIED = 3; - /** @hide */ - public static final int RIL_REG_STATE_UNKNOWN = 4; - /** @hide */ - public static final int RIL_REG_STATE_ROAMING = 5; - /** @hide */ - public static final int RIL_REG_STATE_NOT_REG_EMERGENCY_CALL_ENABLED = 10; - /** @hide */ - public static final int RIL_REG_STATE_SEARCHING_EMERGENCY_CALL_ENABLED = 12; - /** @hide */ - public static final int RIL_REG_STATE_DENIED_EMERGENCY_CALL_ENABLED = 13; - /** @hide */ - public static final int RIL_REG_STATE_UNKNOWN_EMERGENCY_CALL_ENABLED = 14; - /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = { "RIL_RADIO_TECHNOLOGY_" }, @@ -233,22 +192,6 @@ public class ServiceState implements Parcelable { | (1 << (RIL_RADIO_TECHNOLOGY_EVDO_B - 1)) | (1 << (RIL_RADIO_TECHNOLOGY_EHRPD - 1)); - /** - * Available registration states for GSM, UMTS and CDMA. - */ - /** @hide */ - public static final int REGISTRATION_STATE_NOT_REGISTERED_AND_NOT_SEARCHING = 0; - /** @hide */ - public static final int REGISTRATION_STATE_HOME_NETWORK = 1; - /** @hide */ - public static final int REGISTRATION_STATE_NOT_REGISTERED_AND_SEARCHING = 2; - /** @hide */ - public static final int REGISTRATION_STATE_REGISTRATION_DENIED = 3; - /** @hide */ - public static final int REGISTRATION_STATE_UNKNOWN = 4; - /** @hide */ - public static final int REGISTRATION_STATE_ROAMING = 5; - private int mVoiceRegState = STATE_OUT_OF_SERVICE; private int mDataRegState = STATE_OUT_OF_SERVICE; @@ -1372,6 +1315,51 @@ public class ServiceState implements Parcelable { } } + /** @hide */ + public static int networkTypeToRilRadioTechnology(int networkType) { + switch(networkType) { + case TelephonyManager.NETWORK_TYPE_GPRS: + return ServiceState.RIL_RADIO_TECHNOLOGY_GPRS; + case TelephonyManager.NETWORK_TYPE_EDGE: + return ServiceState.RIL_RADIO_TECHNOLOGY_EDGE; + case TelephonyManager.NETWORK_TYPE_UMTS: + return ServiceState.RIL_RADIO_TECHNOLOGY_UMTS; + case TelephonyManager.NETWORK_TYPE_HSDPA: + return ServiceState.RIL_RADIO_TECHNOLOGY_HSDPA; + case TelephonyManager.NETWORK_TYPE_HSUPA: + return ServiceState.RIL_RADIO_TECHNOLOGY_HSUPA; + case TelephonyManager.NETWORK_TYPE_HSPA: + return ServiceState.RIL_RADIO_TECHNOLOGY_HSPA; + case TelephonyManager.NETWORK_TYPE_CDMA: + return ServiceState.RIL_RADIO_TECHNOLOGY_IS95A; + case TelephonyManager.NETWORK_TYPE_1xRTT: + return ServiceState.RIL_RADIO_TECHNOLOGY_1xRTT; + case TelephonyManager.NETWORK_TYPE_EVDO_0: + return ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_0; + case TelephonyManager.NETWORK_TYPE_EVDO_A: + return ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_A; + case TelephonyManager.NETWORK_TYPE_EVDO_B: + return ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_B; + case TelephonyManager.NETWORK_TYPE_EHRPD: + return ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD; + case TelephonyManager.NETWORK_TYPE_LTE: + return ServiceState.RIL_RADIO_TECHNOLOGY_LTE; + case TelephonyManager.NETWORK_TYPE_HSPAP: + return ServiceState.RIL_RADIO_TECHNOLOGY_HSPAP; + case TelephonyManager.NETWORK_TYPE_GSM: + return ServiceState.RIL_RADIO_TECHNOLOGY_GSM; + case TelephonyManager.NETWORK_TYPE_TD_SCDMA: + return ServiceState.RIL_RADIO_TECHNOLOGY_TD_SCDMA; + case TelephonyManager.NETWORK_TYPE_IWLAN: + return ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN; + case TelephonyManager.NETWORK_TYPE_LTE_CA: + return ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA; + default: + return ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN; + } + } + + /** @hide */ public int getDataNetworkType() { return rilRadioTechnologyToNetworkType(mRilDataRadioTechnology); -- GitLab From 3d00d698d7d6c10628076cca8df30adb50c52563 Mon Sep 17 00:00:00 2001 From: fionaxu Date: Mon, 29 Jan 2018 14:08:12 -0800 Subject: [PATCH 054/671] add a current table in CarrierIdProvider restructure CarrierIdProvider into two tables 1. All - a private table which stores a complete mapping of all carriers 2. Current - a public table only stores the carrier identification of the current active subs. require no permission to query. expose the content url to public so that apps could be notified on carrier identity change either on background or foreground. Bug: 72571475 Test: runtest --path CarrierIdProviderTest.java Test: Manual Change-Id: If2a20288e63d25343f5bb582b35564d769a4e13b --- api/current.txt | 7 + .../updates/CarrierIdInstallReceiver.java | 2 +- .../java/android/provider/Telephony.java | 126 ++++++++++++------ 3 files changed, 94 insertions(+), 41 deletions(-) diff --git a/api/current.txt b/api/current.txt index 4c69236aaaf..95fcf260f35 100644 --- a/api/current.txt +++ b/api/current.txt @@ -36678,6 +36678,13 @@ package android.provider { field public static final java.lang.String ADDRESS = "address"; } + public static final class Telephony.CarrierIdentification implements android.provider.BaseColumns { + method public static android.net.Uri getUriForSubscriptionId(int); + field public static final java.lang.String CID = "carrier_id"; + field public static final android.net.Uri CONTENT_URI; + field public static final java.lang.String NAME = "carrier_name"; + } + public static final class Telephony.Carriers implements android.provider.BaseColumns { field public static final java.lang.String APN = "apn"; field public static final java.lang.String AUTH_TYPE = "authtype"; diff --git a/services/core/java/com/android/server/updates/CarrierIdInstallReceiver.java b/services/core/java/com/android/server/updates/CarrierIdInstallReceiver.java index 116fe7f41ba..045081679d8 100644 --- a/services/core/java/com/android/server/updates/CarrierIdInstallReceiver.java +++ b/services/core/java/com/android/server/updates/CarrierIdInstallReceiver.java @@ -33,7 +33,7 @@ public class CarrierIdInstallReceiver extends ConfigUpdateInstallReceiver { @Override protected void postInstall(Context context, Intent intent) { ContentResolver resolver = context.getContentResolver(); - resolver.update(Uri.withAppendedPath(Telephony.CarrierIdentification.CONTENT_URI, + resolver.update(Uri.withAppendedPath(Telephony.CarrierIdentification.All.CONTENT_URI, "update_db"), new ContentValues(), null, null); } } diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java index 8c4572474e6..63263bd3720 100644 --- a/telephony/java/android/provider/Telephony.java +++ b/telephony/java/android/provider/Telephony.java @@ -33,6 +33,7 @@ import android.telephony.Rlog; import android.telephony.ServiceState; import android.telephony.SmsMessage; import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Patterns; @@ -3345,73 +3346,118 @@ public final class Telephony { } /** - * Contains carrier identification information. - * @hide + * Contains carrier identification information for the current subscriptions. + * @see SubscriptionManager#getActiveSubscriptionIdList() */ public static final class CarrierIdentification implements BaseColumns { /** - * Numeric operator ID (as String). {@code MCC + MNC} - *

      Type: TEXT

      - */ - public static final String MCCMNC = "mccmnc"; - - /** - * Group id level 1 (as String). - *

      Type: TEXT

      - */ - public static final String GID1 = "gid1"; - - /** - * Group id level 2 (as String). - *

      Type: TEXT

      - */ - public static final String GID2 = "gid2"; - - /** - * Public Land Mobile Network name. - *

      Type: TEXT

      + * Not instantiable. + * @hide */ - public static final String PLMN = "plmn"; + private CarrierIdentification() {} /** - * Prefix xpattern of IMSI (International Mobile Subscriber Identity). - *

      Type: TEXT

      + * The {@code content://} style URI for this provider. */ - public static final String IMSI_PREFIX_XPATTERN = "imsi_prefix_xpattern"; + public static final Uri CONTENT_URI = Uri.parse("content://carrier_identification"); /** - * Service Provider Name. - *

      Type: TEXT

      + * The authority string for the CarrierIdentification Provider + * @hide */ - public static final String SPN = "spn"; + public static final String AUTHORITY = "carrier_identification"; - /** - * Prefer APN name. - *

      Type: TEXT

      - */ - public static final String APN = "apn"; /** - * Prefix of Integrated Circuit Card Identifier. - *

      Type: TEXT

      + * Generates a content {@link Uri} used to receive updates on carrier identity change + * on the given subscriptionId + *

      + * Use this {@link Uri} with a {@link ContentObserver} to be notified of changes to the + * carrier identity {@link TelephonyManager#getAndroidCarrierIdForSubscription()} + * while your app is running. You can also use a {@link JobService} to ensure your app + * is notified of changes to the {@link Uri} even when it is not running. + * Note, however, that using a {@link JobService} does not guarantee timely delivery of + * updates to the {@link Uri}. + * + * @param subscriptionId the subscriptionId to receive updates on + * @return the Uri used to observe carrier identity changes */ - public static final String ICCID_PREFIX = "iccid_prefix"; + public static Uri getUriForSubscriptionId(int subscriptionId) { + return CONTENT_URI.buildUpon().appendEncodedPath( + String.valueOf(subscriptionId)).build(); + } /** - * User facing carrier name. + * A user facing carrier name. + * @see TelephonyManager#getAndroidCarrierNameForSubscription() *

      Type: TEXT

      */ public static final String NAME = "carrier_name"; /** * A unique carrier id + * @see TelephonyManager#getAndroidCarrierIdForSubscription() *

      Type: INTEGER

      */ public static final String CID = "carrier_id"; /** - * The {@code content://} URI for this table. + * Contains mappings between matching rules with carrier id for all carriers. + * @hide */ - public static final Uri CONTENT_URI = Uri.parse("content://carrier_identification"); + public static final class All implements BaseColumns { + /** + * Numeric operator ID (as String). {@code MCC + MNC} + *

      Type: TEXT

      + */ + public static final String MCCMNC = "mccmnc"; + + /** + * Group id level 1 (as String). + *

      Type: TEXT

      + */ + public static final String GID1 = "gid1"; + + /** + * Group id level 2 (as String). + *

      Type: TEXT

      + */ + public static final String GID2 = "gid2"; + + /** + * Public Land Mobile Network name. + *

      Type: TEXT

      + */ + public static final String PLMN = "plmn"; + + /** + * Prefix xpattern of IMSI (International Mobile Subscriber Identity). + *

      Type: TEXT

      + */ + public static final String IMSI_PREFIX_XPATTERN = "imsi_prefix_xpattern"; + + /** + * Service Provider Name. + *

      Type: TEXT

      + */ + public static final String SPN = "spn"; + + /** + * Prefer APN name. + *

      Type: TEXT

      + */ + public static final String APN = "apn"; + + /** + * Prefix of Integrated Circuit Card Identifier. + *

      Type: TEXT

      + */ + public static final String ICCID_PREFIX = "iccid_prefix"; + + /** + * The {@code content://} URI for this table. + */ + public static final Uri CONTENT_URI = Uri.parse("content://carrier_identification/all"); + } } } -- GitLab From 848458ac4fb04b0f3af859aa2cc99d00a0eaa943 Mon Sep 17 00:00:00 2001 From: Makoto Onuki Date: Mon, 5 Feb 2018 14:52:09 -0800 Subject: [PATCH 055/671] Disable sync job service WTF for now. Bug: 72549144 Test: build and boot Change-Id: I948a5b677ee2f4f8501afbfcd2d9127b779aaebf --- .../com/android/server/content/SyncJobService.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/services/core/java/com/android/server/content/SyncJobService.java b/services/core/java/com/android/server/content/SyncJobService.java index 51499f735bc..9980930d87c 100644 --- a/services/core/java/com/android/server/content/SyncJobService.java +++ b/services/core/java/com/android/server/content/SyncJobService.java @@ -137,11 +137,13 @@ public class SyncJobService extends JobService { + " params=" + jobParametersToString(params)); } } else if (runtime < 10 * 1000) { - // Job stopped too soon. WTF. - wtf("Job " + jobId + " stopped in " + runtime + " ms: " - + " startUptime=" + startUptime - + " nowUptime=" + nowUptime - + " params=" + jobParametersToString(params)); + // This happens too in a normal case too, and it's rather too often. + // Disable it for now. +// // Job stopped too soon. WTF. +// wtf("Job " + jobId + " stopped in " + runtime + " ms: " +// + " startUptime=" + startUptime +// + " nowUptime=" + nowUptime +// + " params=" + jobParametersToString(params)); } mStartedSyncs.delete(jobId); -- GitLab From b119dada29890f646d5f10d1ea6f529fde5b777a Mon Sep 17 00:00:00 2001 From: Arthur Eubanks Date: Mon, 5 Feb 2018 14:56:39 -0800 Subject: [PATCH 056/671] Add "uses-sdk" to AndroidManifest for ActivityManagerPerfTests This allows it to be installed on devices flashed with release builds. Test: on device flashed with release build Test: m ActivityManagerPerfTestsTestApp ActivityManagerPerfTests Test: adb install \ "$OUT"/data/app/ActivityManagerPerfTests/ActivityManagerPerfTests.apk Test: adb install \ "$OUT"/data/app/ActivityManagerPerfTestsTestApp/ActivityManagerPerfTestsTestApp.apk Change-Id: I817c79128d8bee0a424308c32c0092965ada6c1e --- tests/ActivityManagerPerfTests/test-app/AndroidManifest.xml | 3 +++ tests/ActivityManagerPerfTests/tests/AndroidManifest.xml | 3 +++ 2 files changed, 6 insertions(+) diff --git a/tests/ActivityManagerPerfTests/test-app/AndroidManifest.xml b/tests/ActivityManagerPerfTests/test-app/AndroidManifest.xml index 021e3861d88..ec5a9c63e9c 100644 --- a/tests/ActivityManagerPerfTests/test-app/AndroidManifest.xml +++ b/tests/ActivityManagerPerfTests/test-app/AndroidManifest.xml @@ -15,6 +15,9 @@ --> + + -- GitLab From 8c7cb02f1faae349c0653b4b594ce8ea9f305cee Mon Sep 17 00:00:00 2001 From: Lucas Dupin Date: Mon, 5 Feb 2018 10:49:03 -0800 Subject: [PATCH 057/671] Display blanking logic improvements Blank display immediatelly when requested, display power controller will fade out the screen using ColorFade. SysUI just needs to fade the UI back in when we're in control of the transition again. Change-Id: I9d58de3d39c0288f5c82c0c04c86cb43bb3d02c4 Fixes: 72527083 Test: receive notification on AOD, wait. Test: receive notification on AOD, fp unlock Test: wake up, unlock, pull notification shade Test: atest packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java Bug: 71913808 --- packages/SystemUI/res/values/ids.xml | 1 - .../statusbar/phone/ScrimController.java | 79 +++++++++---------- .../systemui/statusbar/phone/ScrimState.java | 7 -- .../statusbar/phone/ScrimControllerTest.java | 11 ++- 4 files changed, 47 insertions(+), 51 deletions(-) diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml index f9aa8216ec1..7537026d1b5 100644 --- a/packages/SystemUI/res/values/ids.xml +++ b/packages/SystemUI/res/values/ids.xml @@ -47,7 +47,6 @@ - diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index 44e0c257ef2..eb4e9b3221e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -30,6 +30,7 @@ import android.os.Handler; import android.os.Trace; import android.util.Log; import android.util.MathUtils; +import android.view.Choreographer; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; @@ -111,7 +112,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, protected static final float SCRIM_IN_FRONT_ALPHA_LOCKED = GRADIENT_SCRIM_ALPHA_BUSY; static final int TAG_KEY_ANIM = R.id.scrim; - static final int TAG_KEY_ANIM_BLANK = R.id.scrim_blanking; private static final int TAG_KEY_ANIM_TARGET = R.id.scrim_target; private static final int TAG_START_ALPHA = R.id.scrim_alpha_start; private static final int TAG_END_ALPHA = R.id.scrim_alpha_end; @@ -166,6 +166,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, private boolean mScreenBlankingCallbackCalled; private Callback mCallback; private boolean mWallpaperSupportsAmbientMode; + private Choreographer.FrameCallback mPendingFrameCallback; private final WakeLock mWakeLock; private boolean mWakeLockHeld; @@ -248,6 +249,11 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, mCurrentInFrontAlpha = state.getFrontAlpha(); mCurrentBehindAlpha = state.getBehindAlpha(); + if (mPendingFrameCallback != null) { + Choreographer.getInstance().removeFrameCallback(mPendingFrameCallback); + mPendingFrameCallback = null; + } + // Showing/hiding the keyguard means that scrim colors have to be switched, not necessary // to do the same when you're just showing the brightness mirror. mNeedsDrawableColorUpdate = state != ScrimState.BRIGHTNESS_MIRROR; @@ -687,11 +693,12 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, } } - final boolean blankingInProgress = mScrimInFront.getTag(TAG_KEY_ANIM_BLANK) != null; - if (mBlankScreen || blankingInProgress) { - if (!blankingInProgress) { - blankDisplay(); - } + if (mPendingFrameCallback != null) { + // Display is off and we're waiting. + return; + } else if (mBlankScreen) { + // Need to blank the display before continuing. + blankDisplay(); return; } else if (!mScreenBlankingCallbackCalled) { // Not blanking the screen. Letting the callback know that we're ready @@ -745,45 +752,33 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, } private void blankDisplay() { - final float initialAlpha = mScrimInFront.getViewAlpha(); - final int initialTint = mScrimInFront.getTint(); - ValueAnimator anim = ValueAnimator.ofFloat(0, 1); - anim.addUpdateListener(animation -> { - final float amount = (float) animation.getAnimatedValue(); - float animAlpha = MathUtils.lerp(initialAlpha, 1, amount); - int animTint = ColorUtils.blendARGB(initialTint, Color.BLACK, amount); - updateScrimColor(mScrimInFront, animAlpha, animTint); - dispatchScrimsVisible(); - }); - anim.setInterpolator(getInterpolator()); - anim.setDuration(mDozeParameters.getPulseInDuration()); - anim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - if (mCallback != null) { - mCallback.onDisplayBlanked(); - mScreenBlankingCallbackCalled = true; - } - Runnable blankingCallback = () -> { - mScrimInFront.setTag(TAG_KEY_ANIM_BLANK, null); - mBlankScreen = false; - // Try again. - updateScrims(); - }; - - // Setting power states can happen after we push out the frame. Make sure we - // stay fully opaque until the power state request reaches the lower levels. - getHandler().postDelayed(blankingCallback, 100); + updateScrimColor(mScrimInFront, 1, Color.BLACK); + // Notify callback that the screen is completely black and we're + // ready to change the display power mode + mPendingFrameCallback = frameTimeNanos -> { + if (mCallback != null) { + mCallback.onDisplayBlanked(); + mScreenBlankingCallbackCalled = true; } - }); - anim.start(); - mScrimInFront.setTag(TAG_KEY_ANIM_BLANK, anim); - // Finish animation if we're already at its final state - if (initialAlpha == 1 && mScrimInFront.getTint() == Color.BLACK) { - anim.end(); - } + Runnable blankingCallback = () -> { + mPendingFrameCallback = null; + mBlankScreen = false; + // Try again. + updateScrims(); + }; + + // Setting power states can happen after we push out the frame. Make sure we + // stay fully opaque until the power state request reaches the lower levels. + getHandler().postDelayed(blankingCallback, 100); + }; + doOnTheNextFrame(mPendingFrameCallback); + } + + @VisibleForTesting + protected void doOnTheNextFrame(Choreographer.FrameCallback callback) { + Choreographer.getInstance().postFrameCallback(callback); } @VisibleForTesting diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java index 314d6aa2bf5..381e4af3185 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java @@ -48,7 +48,6 @@ public enum ScrimState { // set our scrim to black in this frame to avoid flickering and // fade it out afterwards. mBlankScreen = true; - updateScrimColor(mScrimInFront, 1, Color.BLACK); } } else { mAnimationDuration = ScrimController.ANIMATION_DURATION; @@ -86,9 +85,6 @@ public enum ScrimState { AOD(3) { @Override public void prepare(ScrimState previousState) { - if (previousState == ScrimState.PULSING && !mCanControlScreenOff) { - updateScrimColor(mScrimInFront, 1, Color.BLACK); - } final boolean alwaysOnEnabled = mDozeParameters.getAlwaysOn(); final boolean wasPulsing = previousState == ScrimState.PULSING; mBlankScreen = wasPulsing && !mCanControlScreenOff; @@ -115,9 +111,6 @@ public enum ScrimState { && !mKeyguardUpdateMonitor.hasLockscreenWallpaper() ? 0f : 1f; mCurrentBehindTint = Color.BLACK; mBlankScreen = mDisplayRequiresBlanking; - if (mDisplayRequiresBlanking) { - updateScrimColor(mScrimInFront, 1, Color.BLACK); - } } }, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java index 6d2691c899f..43e16dbeaee 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java @@ -38,6 +38,7 @@ import android.os.Looper; import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import android.view.Choreographer; import android.view.View; import com.android.keyguard.KeyguardUpdateMonitor; @@ -374,7 +375,6 @@ public class ScrimControllerTest extends SysuiTestCase { onPreDraw(); // Force finish screen blanking. - endAnimation(mScrimInFront, TAG_KEY_ANIM_BLANK); mHandler.dispatchQueuedMessages(); // Force finish all animations. endAnimation(mScrimBehind, TAG_KEY_ANIM); @@ -401,6 +401,15 @@ public class ScrimControllerTest extends SysuiTestCase { protected WakeLock createWakeLock() { return mWakeLock; } + + /** + * Do not wait for a frame since we're in a test environment. + * @param callback What to execute. + */ + @Override + protected void doOnTheNextFrame(Choreographer.FrameCallback callback) { + callback.doFrame(0); + } } } -- GitLab From ca4c5a6276d3fd443c256856e272007322b5e917 Mon Sep 17 00:00:00 2001 From: Robert Carr Date: Mon, 5 Feb 2018 16:07:55 -0800 Subject: [PATCH 058/671] Ensure surfaces are reparented before destroying parent. In destroyPreservedSurfaceLocked we open and close the global transaction with the intention of ensuring that the reparent contained within is completed before we send the destroy request below. This doesn't work as the global transaction could be held open by another thread, e.g. we may already be in the transaction. Bug: 70530552 Bug: 71499373 Bug: 72894049 Test: Existing tests pass Change-Id: Ie91da3c78eaf8fb52a21ae49ed190b42d92d0083 --- .../java/com/android/server/wm/WindowStateAnimator.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 5c9cfbb7a5a..a76a8d585cb 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -211,6 +211,8 @@ class WindowStateAnimator { private final Rect mTmpSize = new Rect(); + private final SurfaceControl.Transaction mReparentTransaction = new SurfaceControl.Transaction(); + WindowStateAnimator(final WindowState win) { final WindowManagerService service = win.mService; @@ -371,9 +373,9 @@ class WindowStateAnimator { // child layers need to be reparented to the new surface to make this // transparent to the app. if (mWin.mAppToken == null || mWin.mAppToken.isRelaunching() == false) { - SurfaceControl.openTransaction(); - mPendingDestroySurface.reparentChildrenInTransaction(mSurfaceController); - SurfaceControl.closeTransaction(); + mReparentTransaction.reparentChildren(mPendingDestroySurface.mSurfaceControl, + mSurfaceController.mSurfaceControl.getHandle()) + .apply(); } } } -- GitLab From a882886f80bdf08f6ee8812bf59819af180ab9b9 Mon Sep 17 00:00:00 2001 From: Robert Carr Date: Mon, 5 Feb 2018 16:17:36 -0800 Subject: [PATCH 059/671] Rename destroyInTransaction to destroyNotInTransaction. Destroy is not scoped with a transaction. We are using this appropriately but the name has been wrong for a long time. Change-Id: I2bb5d91774a3c0a0b23ef73a26484cc2a2c0386b --- .../java/com/android/server/wm/WindowStateAnimator.java | 6 +++--- .../java/com/android/server/wm/WindowSurfaceController.java | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index a76a8d585cb..dd23b6f2563 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -589,7 +589,7 @@ class WindowStateAnimator { if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) { WindowManagerService.logSurface(mWin, "DESTROY PENDING", true); } - mPendingDestroySurface.destroyInTransaction(); + mPendingDestroySurface.destroyNotInTransaction(); } mPendingDestroySurface = mSurfaceController; } @@ -626,7 +626,7 @@ class WindowStateAnimator { if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) { WindowManagerService.logSurface(mWin, "DESTROY PENDING", true); } - mPendingDestroySurface.destroyInTransaction(); + mPendingDestroySurface.destroyNotInTransaction(); // Don't hide wallpaper if we're destroying a deferred surface // after a surface mode change. if (!mDestroyPreservedSurfaceUponRedraw) { @@ -1421,7 +1421,7 @@ class WindowStateAnimator { void destroySurface() { try { if (mSurfaceController != null) { - mSurfaceController.destroyInTransaction(); + mSurfaceController.destroyNotInTransaction(); } } catch (RuntimeException e) { Slog.w(TAG, "Exception thrown when destroying surface " + this diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java index 2f38556efa7..554a60023af 100644 --- a/services/core/java/com/android/server/wm/WindowSurfaceController.java +++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java @@ -168,7 +168,7 @@ class WindowSurfaceController { } } - void destroyInTransaction() { + void destroyNotInTransaction() { if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) { Slog.i(TAG, "Destroying surface " + this + " called by " + Debug.getCallers(8)); } -- GitLab From a61f94ef827bfbaad7c76e8a297bf8d9ecf290be Mon Sep 17 00:00:00 2001 From: Matthew Ng Date: Mon, 5 Feb 2018 15:55:37 -0800 Subject: [PATCH 060/671] Fixes crash when pinning unfocused task in multiwindow If an app can invoke pin locked mode such as Play video, and is split to the bottom/right and not focused (focus the primary split task), then it will crash trying to start lock task mode. Fix by moving pinned task to front to become focused. Change-Id: Ifd2c91b90d0f9f29111321f6fb2a1c5cf0073c49 Fixes: 72868672 Test: manual --- .../java/com/android/server/am/ActivityManagerService.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 8d1632a3cef..b666d1015c3 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -11283,8 +11283,11 @@ public class ActivityManagerService extends IActivityManager.Stub long ident = Binder.clearCallingIdentity(); try { synchronized (this) { - startLockTaskModeLocked(mStackSupervisor.anyTaskForIdLocked(taskId), - true /* isSystemCaller */); + final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId); + + // When starting lock task mode the stack must be in front and focused + task.getStack().moveToFront("startSystemLockTaskMode"); + startLockTaskModeLocked(task, true /* isSystemCaller */); } } finally { Binder.restoreCallingIdentity(ident); -- GitLab From c3c31a5382af607380ebd5f4209e62fdf1eb45c6 Mon Sep 17 00:00:00 2001 From: Wei Jia Date: Mon, 5 Feb 2018 16:18:27 -0800 Subject: [PATCH 061/671] MediaPlayer2: move MediaPlayer2 native code to libmediaplayer2 Test: MediaPlayer2 plays Bug: 63934228 Change-Id: Ie674b01b6e839d5ca0af76d8b712c395da64b57a --- media/jni/Android.bp | 2 +- media/jni/android_media_MediaPlayer2.cpp | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/media/jni/Android.bp b/media/jni/Android.bp index 7e5f581dd1c..fe2f64fa2d9 100644 --- a/media/jni/Android.bp +++ b/media/jni/Android.bp @@ -127,11 +127,11 @@ cc_library_shared { "liblzma", "libmedia", "libmedia_helper", - "libmedia_player2", "libmedia_player2_util", "libmediadrm", "libmediaextractor", "libmediametrics", + "libmediaplayer2", "libmediautils", "libnativehelper", "libnetd_client", diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp index 27eaed05b04..0eb98f34b6b 100644 --- a/media/jni/android_media_MediaPlayer2.cpp +++ b/media/jni/android_media_MediaPlayer2.cpp @@ -21,15 +21,14 @@ #include -#include #include #include #include -#include #include #include #include #include // for FOURCC definition +#include #include #include #include -- GitLab From 4d298b5df143b32b30d5d442f8743e292ed2147d Mon Sep 17 00:00:00 2001 From: Makoto Onuki Date: Mon, 5 Feb 2018 10:54:58 -0800 Subject: [PATCH 062/671] Don't take a lock on the UI thread Bug: 72945360 Test: atest CtsAlarmManagerTestCases Test: atest CtsBatterySavingTestCases Test: atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java Change-Id: Ifbc72acadf512d70625bbf4e08e960349eeae4d8 --- .../android/server/AlarmManagerService.java | 23 ++-- .../server/ForceAppStandbyTracker.java | 114 +++++++++++++----- .../server/ForceAppStandbyTrackerTest.java | 13 ++ 3 files changed, 110 insertions(+), 40 deletions(-) diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java index f49cd6745e7..64bbbb9b155 100644 --- a/services/core/java/com/android/server/AlarmManagerService.java +++ b/services/core/java/com/android/server/AlarmManagerService.java @@ -3470,10 +3470,15 @@ class AlarmManagerService extends SystemService { public static final int REPORT_ALARMS_ACTIVE = 4; public static final int APP_STANDBY_BUCKET_CHANGED = 5; public static final int APP_STANDBY_PAROLE_CHANGED = 6; + public static final int REMOVE_FOR_STOPPED = 7; public AlarmHandler() { } + public void postRemoveForStopped(int uid) { + obtainMessage(REMOVE_FOR_STOPPED, uid, 0).sendToTarget(); + } + public void handleMessage(Message msg) { switch (msg.what) { case ALARM_EVENT: { @@ -3528,6 +3533,12 @@ class AlarmManagerService extends SystemService { } break; + case REMOVE_FOR_STOPPED: + synchronized (mLock) { + removeForStoppedLocked(msg.arg1); + } + break; + default: // nope, just ignore it break; @@ -3717,10 +3728,8 @@ class AlarmManagerService extends SystemService { } @Override public void onUidGone(int uid, boolean disabled) { - synchronized (mLock) { - if (disabled) { - removeForStoppedLocked(uid); - } + if (disabled) { + mHandler.postRemoveForStopped(uid); } } @@ -3728,10 +3737,8 @@ class AlarmManagerService extends SystemService { } @Override public void onUidIdle(int uid, boolean disabled) { - synchronized (mLock) { - if (disabled) { - removeForStoppedLocked(uid); - } + if (disabled) { + mHandler.postRemoveForStopped(uid); } } diff --git a/services/core/java/com/android/server/ForceAppStandbyTracker.java b/services/core/java/com/android/server/ForceAppStandbyTracker.java index 339101fa79b..100680df637 100644 --- a/services/core/java/com/android/server/ForceAppStandbyTracker.java +++ b/services/core/java/com/android/server/ForceAppStandbyTracker.java @@ -614,48 +614,22 @@ public class ForceAppStandbyTracker { private final class UidObserver extends IUidObserver.Stub { @Override public void onUidStateChanged(int uid, int procState, long procStateSeq) { - synchronized (mLock) { - if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) { - if (removeUidFromArray(mForegroundUids, uid, false)) { - mHandler.notifyUidForegroundStateChanged(uid); - } - } else { - if (addUidToArray(mForegroundUids, uid)) { - mHandler.notifyUidForegroundStateChanged(uid); - } - } - } + mHandler.onUidStateChanged(uid, procState); } @Override - public void onUidGone(int uid, boolean disabled) { - removeUid(uid, true); + public void onUidActive(int uid) { + mHandler.onUidActive(uid); } @Override - public void onUidActive(int uid) { - synchronized (mLock) { - if (addUidToArray(mActiveUids, uid)) { - mHandler.notifyUidActiveStateChanged(uid); - } - } + public void onUidGone(int uid, boolean disabled) { + mHandler.onUidGone(uid, disabled); } @Override public void onUidIdle(int uid, boolean disabled) { - // Just to avoid excessive memcpy, don't remove from the array in this case. - removeUid(uid, false); - } - - private void removeUid(int uid, boolean remove) { - synchronized (mLock) { - if (removeUidFromArray(mActiveUids, uid, remove)) { - mHandler.notifyUidActiveStateChanged(uid); - } - if (removeUidFromArray(mForegroundUids, uid, remove)) { - mHandler.notifyUidForegroundStateChanged(uid); - } - } + mHandler.onUidIdle(uid, disabled); } @Override @@ -740,6 +714,11 @@ public class ForceAppStandbyTracker { private static final int MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED = 9; private static final int MSG_EXEMPT_CHANGED = 10; + private static final int MSG_ON_UID_STATE_CHANGED = 11; + private static final int MSG_ON_UID_ACTIVE = 12; + private static final int MSG_ON_UID_GONE = 13; + private static final int MSG_ON_UID_IDLE = 14; + public MyHandler(Looper looper) { super(looper); } @@ -790,6 +769,22 @@ public class ForceAppStandbyTracker { obtainMessage(MSG_USER_REMOVED, userId, 0).sendToTarget(); } + public void onUidStateChanged(int uid, int procState) { + obtainMessage(MSG_ON_UID_STATE_CHANGED, uid, procState).sendToTarget(); + } + + public void onUidActive(int uid) { + obtainMessage(MSG_ON_UID_ACTIVE, uid, 0).sendToTarget(); + } + + public void onUidGone(int uid, boolean disabled) { + obtainMessage(MSG_ON_UID_GONE, uid, disabled ? 1 : 0).sendToTarget(); + } + + public void onUidIdle(int uid, boolean disabled) { + obtainMessage(MSG_ON_UID_IDLE, uid, disabled ? 1 : 0).sendToTarget(); + } + @Override public void handleMessage(Message msg) { switch (msg.what) { @@ -883,6 +878,61 @@ public class ForceAppStandbyTracker { case MSG_USER_REMOVED: handleUserRemoved(msg.arg1); return; + + case MSG_ON_UID_STATE_CHANGED: + handleUidStateChanged(msg.arg1, msg.arg2); + return; + case MSG_ON_UID_ACTIVE: + handleUidActive(msg.arg1); + return; + case MSG_ON_UID_GONE: + handleUidGone(msg.arg1, msg.arg1 != 0); + return; + case MSG_ON_UID_IDLE: + handleUidIdle(msg.arg1, msg.arg1 != 0); + return; + } + } + + public void handleUidStateChanged(int uid, int procState) { + synchronized (mLock) { + if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) { + if (removeUidFromArray(mForegroundUids, uid, false)) { + mHandler.notifyUidForegroundStateChanged(uid); + } + } else { + if (addUidToArray(mForegroundUids, uid)) { + mHandler.notifyUidForegroundStateChanged(uid); + } + } + } + } + + public void handleUidActive(int uid) { + synchronized (mLock) { + if (addUidToArray(mActiveUids, uid)) { + mHandler.notifyUidActiveStateChanged(uid); + } + } + } + + public void handleUidGone(int uid, boolean disabled) { + removeUid(uid, true); + } + + public void handleUidIdle(int uid, boolean disabled) { + // Just to avoid excessive memcpy, don't remove from the array in this case. + removeUid(uid, false); + } + + private void removeUid(int uid, boolean remove) { + synchronized (mLock) { + if (removeUidFromArray(mActiveUids, uid, remove)) { + mHandler.notifyUidActiveStateChanged(uid); + } + if (removeUidFromArray(mForegroundUids, uid, remove)) { + mHandler.notifyUidForegroundStateChanged(uid); + } } } } diff --git a/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java b/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java index 2c50f227218..a499472d197 100644 --- a/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java +++ b/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java @@ -343,6 +343,7 @@ public class ForceAppStandbyTrackerTest { assertTrue(instance.isUidActive(Process.SYSTEM_UID)); mIUidObserver.onUidActive(UID_1); + waitUntilMainHandlerDrain(); areRestricted(instance, UID_1, PACKAGE_1, NONE); areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS); areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE); @@ -350,6 +351,7 @@ public class ForceAppStandbyTrackerTest { assertFalse(instance.isUidActive(UID_2)); mIUidObserver.onUidGone(UID_1, /*disable=*/ false); + waitUntilMainHandlerDrain(); areRestricted(instance, UID_1, PACKAGE_1, JOBS_AND_ALARMS); areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS); areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE); @@ -357,11 +359,13 @@ public class ForceAppStandbyTrackerTest { assertFalse(instance.isUidActive(UID_2)); mIUidObserver.onUidActive(UID_1); + waitUntilMainHandlerDrain(); areRestricted(instance, UID_1, PACKAGE_1, NONE); areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS); areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE); mIUidObserver.onUidIdle(UID_1, /*disable=*/ false); + waitUntilMainHandlerDrain(); areRestricted(instance, UID_1, PACKAGE_1, JOBS_AND_ALARMS); areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS); areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE); @@ -467,6 +471,7 @@ public class ForceAppStandbyTrackerTest { mIUidObserver.onUidActive(UID_1); + waitUntilMainHandlerDrain(); assertTrue(instance.isUidActive(UID_1)); assertFalse(instance.isUidActive(UID_2)); assertTrue(instance.isUidActive(Process.SYSTEM_UID)); @@ -479,6 +484,7 @@ public class ForceAppStandbyTrackerTest { mIUidObserver.onUidStateChanged(UID_2, ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE, 0); + waitUntilMainHandlerDrain(); assertTrue(instance.isUidActive(UID_1)); assertFalse(instance.isUidActive(UID_2)); assertTrue(instance.isUidActive(Process.SYSTEM_UID)); @@ -491,6 +497,7 @@ public class ForceAppStandbyTrackerTest { mIUidObserver.onUidStateChanged(UID_1, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0); + waitUntilMainHandlerDrain(); assertTrue(instance.isUidActive(UID_1)); assertFalse(instance.isUidActive(UID_2)); assertTrue(instance.isUidActive(Process.SYSTEM_UID)); @@ -501,6 +508,7 @@ public class ForceAppStandbyTrackerTest { mIUidObserver.onUidGone(UID_1, true); + waitUntilMainHandlerDrain(); assertFalse(instance.isUidActive(UID_1)); assertFalse(instance.isUidActive(UID_2)); assertTrue(instance.isUidActive(Process.SYSTEM_UID)); @@ -511,6 +519,7 @@ public class ForceAppStandbyTrackerTest { mIUidObserver.onUidIdle(UID_2, true); + waitUntilMainHandlerDrain(); assertFalse(instance.isUidActive(UID_1)); assertFalse(instance.isUidActive(UID_2)); assertTrue(instance.isUidActive(Process.SYSTEM_UID)); @@ -522,6 +531,7 @@ public class ForceAppStandbyTrackerTest { mIUidObserver.onUidStateChanged(UID_1, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND, 0); + waitUntilMainHandlerDrain(); assertFalse(instance.isUidActive(UID_1)); assertFalse(instance.isUidActive(UID_2)); assertTrue(instance.isUidActive(Process.SYSTEM_UID)); @@ -533,6 +543,7 @@ public class ForceAppStandbyTrackerTest { mIUidObserver.onUidStateChanged(UID_1, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0); + waitUntilMainHandlerDrain(); assertFalse(instance.isUidActive(UID_1)); assertFalse(instance.isUidActive(UID_2)); assertTrue(instance.isUidActive(Process.SYSTEM_UID)); @@ -1037,6 +1048,8 @@ public class ForceAppStandbyTrackerTest { mIUidObserver.onUidActive(UID_1); mIUidObserver.onUidActive(UID_10_1); + waitUntilMainHandlerDrain(); + setAppOps(UID_2, PACKAGE_2, true); setAppOps(UID_10_2, PACKAGE_2, true); -- GitLab From 9b0ab491f44e777e024ac0336dcc152af92e631d Mon Sep 17 00:00:00 2001 From: Bo Zhu Date: Mon, 5 Feb 2018 16:43:09 -0800 Subject: [PATCH 063/671] Change enum MustExist in CertUtils to IntDef integers Test: adb shell am instrument -w -e package \ com.android.server.locksettings.recoverablekeystore \ com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner Change-Id: I5ebb52c86189f813db688e075ac8b2144d938102 --- .../certificate/CertUtils.java | 26 +++++++++++-------- .../certificate/CertXml.java | 8 +++--- .../certificate/SigXml.java | 6 ++--- .../certificate/CertUtilsTest.java | 12 ++++----- 4 files changed, 28 insertions(+), 24 deletions(-) diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertUtils.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertUtils.java index 985f5b61dc3..fea6733dd1f 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertUtils.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertUtils.java @@ -18,6 +18,7 @@ package com.android.server.locksettings.recoverablekeystore.certificate; import static javax.xml.xpath.XPathConstants.NODESET; +import android.annotation.IntDef; import android.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; @@ -25,6 +26,8 @@ import com.android.internal.annotations.VisibleForTesting; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; @@ -72,13 +75,14 @@ final class CertUtils { private static final String CERT_STORE_ALG = "Collection"; private static final String SIGNATURE_ALG = "SHA256withRSA"; - private CertUtils() {} + @Retention(RetentionPolicy.SOURCE) + @IntDef({MUST_EXIST_UNENFORCED, MUST_EXIST_EXACTLY_ONE, MUST_EXIST_AT_LEAST_ONE}) + @interface MustExist {} + static final int MUST_EXIST_UNENFORCED = 0; + static final int MUST_EXIST_EXACTLY_ONE = 1; + static final int MUST_EXIST_AT_LEAST_ONE = 2; - enum MustExist { - FALSE, - EXACTLY_ONE, - AT_LEAST_ONE, - } + private CertUtils() {} /** * Decodes a byte array containing an encoded X509 certificate. @@ -159,7 +163,7 @@ final class CertUtils { * @return a list of strings that are the text contents of the child nodes * @throws CertParsingException if any parsing error occurs */ - static List getXmlNodeContents(MustExist mustExist, Element rootNode, + static List getXmlNodeContents(@MustExist int mustExist, Element rootNode, String... nodeTags) throws CertParsingException { String expression = String.join("/", nodeTags); @@ -173,10 +177,10 @@ final class CertUtils { } switch (mustExist) { - case FALSE: + case MUST_EXIST_UNENFORCED: break; - case EXACTLY_ONE: + case MUST_EXIST_EXACTLY_ONE: if (nodeList.getLength() != 1) { throw new CertParsingException( "The XML file must contain exactly one node with the path " @@ -184,7 +188,7 @@ final class CertUtils { } break; - case AT_LEAST_ONE: + case MUST_EXIST_AT_LEAST_ONE: if (nodeList.getLength() == 0) { throw new CertParsingException( "The XML file must contain at least one node with the path " @@ -194,7 +198,7 @@ final class CertUtils { default: throw new UnsupportedOperationException( - "This enum value of MustExist is not supported: " + mustExist); + "This value of MustExist is not supported: " + mustExist); } List result = new ArrayList<>(); diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertXml.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertXml.java index 2c04a861abb..c62a31e24fb 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertXml.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertXml.java @@ -129,7 +129,7 @@ public final class CertXml { private static long parseSerial(Element rootNode) throws CertParsingException { List contents = CertUtils.getXmlNodeContents( - CertUtils.MustExist.EXACTLY_ONE, + CertUtils.MUST_EXIST_EXACTLY_ONE, rootNode, METADATA_NODE_TAG, METADATA_SERIAL_NODE_TAG); @@ -139,7 +139,7 @@ public final class CertXml { private static long parseRefreshInterval(Element rootNode) throws CertParsingException { List contents = CertUtils.getXmlNodeContents( - CertUtils.MustExist.EXACTLY_ONE, + CertUtils.MUST_EXIST_EXACTLY_ONE, rootNode, METADATA_NODE_TAG, METADATA_REFRESH_INTERVAL_NODE_TAG); @@ -150,7 +150,7 @@ public final class CertXml { throws CertParsingException { List contents = CertUtils.getXmlNodeContents( - CertUtils.MustExist.FALSE, + CertUtils.MUST_EXIST_UNENFORCED, rootNode, INTERMEDIATE_CERT_LIST_TAG, INTERMEDIATE_CERT_ITEM_TAG); @@ -165,7 +165,7 @@ public final class CertXml { throws CertParsingException { List contents = CertUtils.getXmlNodeContents( - CertUtils.MustExist.AT_LEAST_ONE, + CertUtils.MUST_EXIST_AT_LEAST_ONE, rootNode, ENDPOINT_CERT_LIST_TAG, ENDPOINT_CERT_ITEM_TAG); diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/SigXml.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/SigXml.java index 878fc6edf77..e75be857425 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/SigXml.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/SigXml.java @@ -94,7 +94,7 @@ public final class SigXml { throws CertParsingException { List contents = CertUtils.getXmlNodeContents( - CertUtils.MustExist.FALSE, + CertUtils.MUST_EXIST_UNENFORCED, rootNode, INTERMEDIATE_CERT_LIST_TAG, INTERMEDIATE_CERT_ITEM_TAG); @@ -108,14 +108,14 @@ public final class SigXml { private static X509Certificate parseSignerCert(Element rootNode) throws CertParsingException { List contents = CertUtils.getXmlNodeContents( - CertUtils.MustExist.EXACTLY_ONE, rootNode, SIGNER_CERT_NODE_TAG); + CertUtils.MUST_EXIST_EXACTLY_ONE, rootNode, SIGNER_CERT_NODE_TAG); return CertUtils.decodeCert(CertUtils.decodeBase64(contents.get(0))); } private static byte[] parseFileSignature(Element rootNode) throws CertParsingException { List contents = CertUtils.getXmlNodeContents( - CertUtils.MustExist.EXACTLY_ONE, rootNode, SIGNATURE_NODE_TAG); + CertUtils.MUST_EXIST_EXACTLY_ONE, rootNode, SIGNATURE_NODE_TAG); return CertUtils.decodeBase64(contents.get(0)); } } diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/certificate/CertUtilsTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/certificate/CertUtilsTest.java index ac6d29395a8..d08dab4752d 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/certificate/CertUtilsTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/certificate/CertUtilsTest.java @@ -146,14 +146,14 @@ public final class CertUtilsTest { @Test public void getXmlNodeContents_singleLevel_succeeds() throws Exception { Element root = CertUtils.getXmlRootNode(XML_STR.getBytes(UTF_8)); - assertThat(CertUtils.getXmlNodeContents(CertUtils.MustExist.FALSE, root, "node1")) + assertThat(CertUtils.getXmlNodeContents(CertUtils.MUST_EXIST_UNENFORCED, root, "node1")) .containsExactly("node1-1", "node1-2"); } @Test public void getXmlNodeContents_multipleLevels_succeeds() throws Exception { Element root = CertUtils.getXmlRootNode(XML_STR.getBytes(UTF_8)); - assertThat(CertUtils.getXmlNodeContents(CertUtils.MustExist.FALSE, root, "node2", "node1")) + assertThat(CertUtils.getXmlNodeContents(CertUtils.MUST_EXIST_UNENFORCED, root, "node2", "node1")) .containsExactly("node2-node1-1", "node2-node1-2", "node2-node1-3"); } @@ -162,7 +162,7 @@ public final class CertUtilsTest { Element root = CertUtils.getXmlRootNode(XML_STR.getBytes(UTF_8)); assertThat( CertUtils.getXmlNodeContents( - CertUtils.MustExist.FALSE, root, "node2", "node-not-exist")) + CertUtils.MUST_EXIST_UNENFORCED, root, "node2", "node-not-exist")) .isEmpty(); } @@ -174,7 +174,7 @@ public final class CertUtilsTest { CertParsingException.class, () -> CertUtils.getXmlNodeContents( - CertUtils.MustExist.AT_LEAST_ONE, root, "node2", + CertUtils.MUST_EXIST_AT_LEAST_ONE, root, "node2", "node-not-exist")); assertThat(expected.getMessage()).contains("must contain at least one"); } @@ -187,7 +187,7 @@ public final class CertUtilsTest { CertParsingException.class, () -> CertUtils.getXmlNodeContents( - CertUtils.MustExist.EXACTLY_ONE, root, "node-not-exist", + CertUtils.MUST_EXIST_EXACTLY_ONE, root, "node-not-exist", "node1")); assertThat(expected.getMessage()).contains("must contain exactly one"); } @@ -200,7 +200,7 @@ public final class CertUtilsTest { CertParsingException.class, () -> CertUtils.getXmlNodeContents( - CertUtils.MustExist.EXACTLY_ONE, root, "node2", "node1")); + CertUtils.MUST_EXIST_EXACTLY_ONE, root, "node2", "node1")); assertThat(expected.getMessage()).contains("must contain exactly one"); } -- GitLab From 47e7cfd8b5813e89ebd719d2d42a4fac9615c2c5 Mon Sep 17 00:00:00 2001 From: Lucas Dupin Date: Mon, 5 Feb 2018 16:53:28 -0800 Subject: [PATCH 064/671] Ignore more yellows and greens Updated palettes to ignore a broader range or yellows, greens and adjacent colors. Change-Id: Ied6c3ea0cb917a19548483a42d66f1f4b4fee4f7 Fixes: 68683213 Test: atest tests/Internal/src/com/android/internal/colorextraction/types/TonalTest.java --- .../internal/colorextraction/types/Tonal.java | 13 ++++---- core/res/res/xml/color_extraction.xml | 30 +++++++++++-------- .../colorextraction/types/TonalTest.java | 15 ++++++++++ 3 files changed, 40 insertions(+), 18 deletions(-) diff --git a/core/java/com/android/internal/colorextraction/types/Tonal.java b/core/java/com/android/internal/colorextraction/types/Tonal.java index 9b7383fcbe8..7b25a0691c4 100644 --- a/core/java/com/android/internal/colorextraction/types/Tonal.java +++ b/core/java/com/android/internal/colorextraction/types/Tonal.java @@ -405,12 +405,13 @@ public class Tonal implements ExtractionType { return v - (float) Math.floor(v); } - static class TonalPalette { - final float[] h; - final float[] s; - final float[] l; - final float minHue; - final float maxHue; + @VisibleForTesting + public static class TonalPalette { + public final float[] h; + public final float[] s; + public final float[] l; + public final float minHue; + public final float maxHue; TonalPalette(float[] h, float[] s, float[] l) { if (h.length != s.length || s.length != l.length) { diff --git a/core/res/res/xml/color_extraction.xml b/core/res/res/xml/color_extraction.xml index 7d52b20f761..93ab0ff2675 100644 --- a/core/res/res/xml/color_extraction.xml +++ b/core/res/res/xml/color_extraction.xml @@ -257,52 +257,58 @@ + l="0.2, 0.643"/> + l="0.173, 0.38"/> + l="0.231, 0.48"/> + l="0.15, 0.40"/> + l="0.15, 0.42"/> + + l="0.36, 0.65"/> + + l="0.388, 0.67"/> + l="0.424, 0.58"/> + l="0.37, 0.65"/> + l="0.435, 0.58"/> + l="0.43, 0.641"/> 1); } + @Test + public void tonal_rangeTest() { + Tonal.ConfigParser config = new Tonal.ConfigParser(InstrumentationRegistry.getContext()); + for (Tonal.TonalPalette palette : config.getTonalPalettes()) { + assertTrue("minHue should be >= to 0.", palette.minHue >= 0); + assertTrue("maxHue should be <= to 360.", palette.maxHue <= 360); + + assertTrue("S should be >= to 0.", palette.s[0] >= 0); + assertTrue("S should be <= to 1.", palette.s[1] <= 1); + + assertTrue("L should be >= to 0.", palette.l[0] >= 0); + assertTrue("L should be <= to 1.", palette.l[1] <= 1); + } + } + @Test public void tonal_blacklistTest() { // Make sure that palette generation will fail. -- GitLab From 7ed7147d99bf218551d023e1b12eab46d812d554 Mon Sep 17 00:00:00 2001 From: Jean-Michel Trivi Date: Fri, 2 Feb 2018 16:52:09 -0800 Subject: [PATCH 065/671] AudioService: vol event for ext volume controller on volume thread When dispatching a volume event to an external volume controller, do it on AudioService's volume handler thread, not on the input thread. Bug: 63906162 Test: none Change-Id: I4eeddd84caf630dca91a819ca1b4fcecc49d0048 --- .../android/server/audio/AudioService.java | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 1825db8647b..42a3dc526a4 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -23,6 +23,7 @@ import static android.media.AudioManager.RINGER_MODE_VIBRATE; import static android.os.Process.FIRST_APPLICATION_UID; import android.Manifest; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityManagerInternal; @@ -240,6 +241,7 @@ public class AudioService extends IAudioService.Stub private static final int MSG_DYN_POLICY_MIX_STATE_UPDATE = 25; private static final int MSG_INDICATE_SYSTEM_READY = 26; private static final int MSG_ACCESSORY_PLUG_MEDIA_UNMUTE = 27; + private static final int MSG_NOTIFY_VOL_EVENT = 28; // start of messages handled under wakelock // these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(), // and not with sendMsg(..., ..., SENDMSG_QUEUE, ...) @@ -1334,11 +1336,9 @@ public class AudioService extends IAudioService.Stub extVolCtlr = mExtVolumeController; } if (extVolCtlr != null) { - try { - mExtVolumeController.notifyVolumeAdjust(direction); - } catch(RemoteException e) { - // nothing we can do about this. Do not log error, too much potential for spam - } + sendMsg(mAudioHandler, MSG_NOTIFY_VOL_EVENT, SENDMSG_QUEUE, + direction, 0 /*ignored*/, + extVolCtlr, 0 /*delay*/); } else { adjustSuggestedStreamVolume(direction, suggestedStreamType, flags, callingPackage, caller, Binder.getCallingUid()); @@ -5054,6 +5054,15 @@ public class AudioService extends IAudioService.Stub state); } + private void onNotifyVolumeEvent(@NonNull IAudioPolicyCallback apc, + @AudioManager.VolumeAdjustment int direction) { + try { + apc.notifyVolumeAdjust(direction); + } catch(Exception e) { + // nothing we can do about this. Do not log error, too much potential for spam + } + } + @Override public void handleMessage(Message msg) { switch (msg.what) { @@ -5216,6 +5225,10 @@ public class AudioService extends IAudioService.Stub case MSG_DYN_POLICY_MIX_STATE_UPDATE: onDynPolicyMixStateUpdate((String) msg.obj, msg.arg1); break; + + case MSG_NOTIFY_VOL_EVENT: + onNotifyVolumeEvent((IAudioPolicyCallback) msg.obj, msg.arg1); + break; } } } -- GitLab From 1187590da38457809dd368d4901c9c47ac5a6958 Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Mon, 23 Jan 2017 12:58:11 -0800 Subject: [PATCH 066/671] Replace AssetManager with AssetManager2 implementation Test: atest CtsContentTestCases:android.content.res.cts Test: make libandroidfw_tests Change-Id: I2bb6d7656d2516d371e83e541ed02f91405f6d94 --- config/hiddenapi-light-greylist.txt | 16 - .../android/content/pm/PackageParser.java | 19 +- core/java/android/content/res/ApkAssets.java | 221 ++ .../android/content/res/AssetManager.java | 1344 +++++--- core/java/android/content/res/Resources.java | 9 +- .../android/content/res/ResourcesImpl.java | 22 +- core/java/android/content/res/TypedArray.java | 185 +- core/java/android/content/res/XmlBlock.java | 8 +- core/jni/Android.bp | 3 +- core/jni/AndroidRuntime.cpp | 2 + core/jni/android/graphics/FontFamily.cpp | 29 +- core/jni/android_app_NativeActivity.cpp | 2 +- core/jni/android_content_res_ApkAssets.cpp | 150 + core/jni/android_util_AssetManager.cpp | 2892 +++++++---------- .../android_util_AssetManager.h | 15 +- libs/androidfw/AssetManager2.cpp | 6 +- libs/androidfw/AttributeResolution.cpp | 263 +- libs/androidfw/LoadedArsc.cpp | 2 - .../include/androidfw/AttributeFinder.h | 6 + .../include/androidfw/AttributeResolution.h | 11 +- libs/androidfw/include/androidfw/LoadedArsc.h | 13 +- libs/androidfw/include/androidfw/MutexGuard.h | 101 + .../tests/AttributeResolution_test.cpp | 39 +- libs/androidfw/tests/BenchmarkHelpers.cpp | 4 +- native/android/asset_manager.cpp | 28 +- rs/jni/android_renderscript_RenderScript.cpp | 30 +- 26 files changed, 2903 insertions(+), 2517 deletions(-) create mode 100644 core/java/android/content/res/ApkAssets.java create mode 100644 core/jni/android_content_res_ApkAssets.cpp create mode 100644 libs/androidfw/include/androidfw/MutexGuard.h diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt index 58936fcc445..c173c2c515b 100644 --- a/config/hiddenapi-light-greylist.txt +++ b/config/hiddenapi-light-greylist.txt @@ -220,28 +220,13 @@ Landroid/content/pm/PackageParser;->parsePackage(Ljava/io/File;IZ)Landroid/conte Landroid/content/pm/UserInfo;->id:I Landroid/content/pm/UserInfo;->isPrimary()Z Landroid/content/res/AssetManager;->addAssetPath(Ljava/lang/String;)I -Landroid/content/res/AssetManager;->addAssetPaths([Ljava/lang/String;)[I -Landroid/content/res/AssetManager;->applyStyle(JIIJ[IIJJ)V -Landroid/content/res/AssetManager;->getArraySize(I)I Landroid/content/res/AssetManager;->getAssignedPackageIdentifiers()Landroid/util/SparseArray; -Landroid/content/res/AssetManager;->getCookieName(I)Ljava/lang/String; Landroid/content/res/AssetManager;->getResourceBagText(II)Ljava/lang/CharSequence; -Landroid/content/res/AssetManager;->loadResourceBagValue(IILandroid/util/TypedValue;Z)I -Landroid/content/res/AssetManager;->loadResourceValue(ISLandroid/util/TypedValue;Z)I -Landroid/content/res/AssetManager;->loadThemeAttributeValue(JILandroid/util/TypedValue;Z)I Landroid/content/res/AssetManager;->mObject:J -Landroid/content/res/AssetManager;->openNonAssetFdNative(ILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor; Landroid/content/res/AssetManager;->openNonAsset(ILjava/lang/String;I)Ljava/io/InputStream; Landroid/content/res/AssetManager;->openNonAsset(ILjava/lang/String;)Ljava/io/InputStream; Landroid/content/res/AssetManager;->openNonAsset(Ljava/lang/String;I)Ljava/io/InputStream; Landroid/content/res/AssetManager;->openNonAsset(Ljava/lang/String;)Ljava/io/InputStream; -Landroid/content/res/AssetManager;->openNonAssetNative(ILjava/lang/String;I)J -Landroid/content/res/AssetManager;->openXmlAssetNative(ILjava/lang/String;)J -Landroid/content/res/AssetManager;->resolveAttrs(JII[I[I[I[I)Z -Landroid/content/res/AssetManager;->retrieveArray(I[I)I -Landroid/content/res/AssetManager;->retrieveAttributes(J[I[I[I)Z -Landroid/content/res/AssetManager;->STYLE_NUM_ENTRIES:I -Landroid/content/res/AssetManager;->STYLE_RESOURCE_ID:I Landroid/content/res/DrawableCache;->getInstance(JLandroid/content/res/Resources;Landroid/content/res/Resources$Theme;)Landroid/graphics/drawable/Drawable; Landroid/content/res/Resources;->getCompatibilityInfo()Landroid/content/res/CompatibilityInfo; Landroid/content/res/ResourcesImpl;->mAccessLock:Ljava/lang/Object; @@ -271,7 +256,6 @@ Landroid/content/res/TypedArray;->mResources:Landroid/content/res/Resources; Landroid/content/res/TypedArray;->mTheme:Landroid/content/res/Resources$Theme; Landroid/content/res/TypedArray;->mValue:Landroid/util/TypedValue; Landroid/content/res/TypedArray;->mXml:Landroid/content/res/XmlBlock$Parser; -Landroid/content/res/XmlBlock;->close()V Landroid/content/res/XmlBlock;->newParser()Landroid/content/res/XmlResourceParser; Landroid/content/res/XmlBlock$Parser;->mParseState:J Landroid/content/SyncStatusInfo;->lastSuccessTime:J diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 2da893771d9..258602ffa23 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -54,6 +54,7 @@ import android.content.pm.PackageParserCacheHelper.WriteHelper; import android.content.pm.split.DefaultSplitAssetLoader; import android.content.pm.split.SplitAssetDependencyLoader; import android.content.pm.split.SplitAssetLoader; +import android.content.res.ApkAssets; import android.content.res.AssetManager; import android.content.res.Configuration; import android.content.res.Resources; @@ -1596,21 +1597,19 @@ public class PackageParser { int flags) throws PackageParserException { final String apkPath = fd != null ? debugPathName : apkFile.getAbsolutePath(); - AssetManager assets = null; + ApkAssets apkAssets = null; XmlResourceParser parser = null; try { - assets = newConfiguredAssetManager(); - int cookie = fd != null - ? assets.addAssetFd(fd, debugPathName) : assets.addAssetPath(apkPath); - if (cookie == 0) { + try { + apkAssets = fd != null + ? ApkAssets.loadFromFd(fd, debugPathName, false, false) + : ApkAssets.loadFromPath(apkPath); + } catch (IOException e) { throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, "Failed to parse " + apkPath); } - final DisplayMetrics metrics = new DisplayMetrics(); - metrics.setToDefaults(); - - parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME); + parser = apkAssets.openXml(ANDROID_MANIFEST_FILENAME); final SigningDetails signingDetails; if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) { @@ -1637,7 +1636,7 @@ public class PackageParser { "Failed to parse " + apkPath, e); } finally { IoUtils.closeQuietly(parser); - IoUtils.closeQuietly(assets); + IoUtils.closeQuietly(apkAssets); } } diff --git a/core/java/android/content/res/ApkAssets.java b/core/java/android/content/res/ApkAssets.java new file mode 100644 index 00000000000..b087c48d8d4 --- /dev/null +++ b/core/java/android/content/res/ApkAssets.java @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.content.res; + +import android.annotation.NonNull; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.Preconditions; + +import java.io.FileDescriptor; +import java.io.IOException; + +/** + * The loaded, immutable, in-memory representation of an APK. + * + * The main implementation is native C++ and there is very little API surface exposed here. The APK + * is mainly accessed via {@link AssetManager}. + * + * Since the ApkAssets instance is immutable, it can be reused and shared across AssetManagers, + * making the creation of AssetManagers very cheap. + * @hide + */ +public final class ApkAssets implements AutoCloseable { + @GuardedBy("this") private long mNativePtr; + @GuardedBy("this") private StringBlock mStringBlock; + + /** + * Creates a new ApkAssets instance from the given path on disk. + * + * @param path The path to an APK on disk. + * @return a new instance of ApkAssets. + * @throws IOException if a disk I/O error or parsing error occurred. + */ + public static @NonNull ApkAssets loadFromPath(@NonNull String path) throws IOException { + return new ApkAssets(path, false /*system*/, false /*forceSharedLib*/, false /*overlay*/); + } + + /** + * Creates a new ApkAssets instance from the given path on disk. + * + * @param path The path to an APK on disk. + * @param system When true, the APK is loaded as a system APK (framework). + * @return a new instance of ApkAssets. + * @throws IOException if a disk I/O error or parsing error occurred. + */ + public static @NonNull ApkAssets loadFromPath(@NonNull String path, boolean system) + throws IOException { + return new ApkAssets(path, system, false /*forceSharedLib*/, false /*overlay*/); + } + + /** + * Creates a new ApkAssets instance from the given path on disk. + * + * @param path The path to an APK on disk. + * @param system When true, the APK is loaded as a system APK (framework). + * @param forceSharedLibrary When true, any packages within the APK with package ID 0x7f are + * loaded as a shared library. + * @return a new instance of ApkAssets. + * @throws IOException if a disk I/O error or parsing error occurred. + */ + public static @NonNull ApkAssets loadFromPath(@NonNull String path, boolean system, + boolean forceSharedLibrary) throws IOException { + return new ApkAssets(path, system, forceSharedLibrary, false /*overlay*/); + } + + /** + * Creates a new ApkAssets instance from the given file descriptor. Not for use by applications. + * + * Performs a dup of the underlying fd, so you must take care of still closing + * the FileDescriptor yourself (and can do that whenever you want). + * + * @param fd The FileDescriptor of an open, readable APK. + * @param friendlyName The friendly name used to identify this ApkAssets when logging. + * @param system When true, the APK is loaded as a system APK (framework). + * @param forceSharedLibrary When true, any packages within the APK with package ID 0x7f are + * loaded as a shared library. + * @return a new instance of ApkAssets. + * @throws IOException if a disk I/O error or parsing error occurred. + */ + public static @NonNull ApkAssets loadFromFd(@NonNull FileDescriptor fd, + @NonNull String friendlyName, boolean system, boolean forceSharedLibrary) + throws IOException { + return new ApkAssets(fd, friendlyName, system, forceSharedLibrary); + } + + /** + * Creates a new ApkAssets instance from the IDMAP at idmapPath. The overlay APK path + * is encoded within the IDMAP. + * + * @param idmapPath Path to the IDMAP of an overlay APK. + * @param system When true, the APK is loaded as a system APK (framework). + * @return a new instance of ApkAssets. + * @throws IOException if a disk I/O error or parsing error occurred. + */ + public static @NonNull ApkAssets loadOverlayFromPath(@NonNull String idmapPath, boolean system) + throws IOException { + return new ApkAssets(idmapPath, system, false /*forceSharedLibrary*/, true /*overlay*/); + } + + private ApkAssets(@NonNull String path, boolean system, boolean forceSharedLib, boolean overlay) + throws IOException { + Preconditions.checkNotNull(path, "path"); + mNativePtr = nativeLoad(path, system, forceSharedLib, overlay); + mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/); + } + + private ApkAssets(@NonNull FileDescriptor fd, @NonNull String friendlyName, boolean system, + boolean forceSharedLib) throws IOException { + Preconditions.checkNotNull(fd, "fd"); + Preconditions.checkNotNull(friendlyName, "friendlyName"); + mNativePtr = nativeLoadFromFd(fd, friendlyName, system, forceSharedLib); + mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/); + } + + @NonNull String getAssetPath() { + synchronized (this) { + ensureValidLocked(); + return nativeGetAssetPath(mNativePtr); + } + } + + CharSequence getStringFromPool(int idx) { + synchronized (this) { + ensureValidLocked(); + return mStringBlock.get(idx); + } + } + + /** + * Retrieve a parser for a compiled XML file. This is associated with a single APK and + * NOT a full AssetManager. This means that shared-library references will not be + * dynamically assigned runtime package IDs. + * + * @param fileName The path to the file within the APK. + * @return An XmlResourceParser. + * @throws IOException if the file was not found or an error occurred retrieving it. + */ + public @NonNull XmlResourceParser openXml(@NonNull String fileName) throws IOException { + Preconditions.checkNotNull(fileName, "fileName"); + synchronized (this) { + ensureValidLocked(); + long nativeXmlPtr = nativeOpenXml(mNativePtr, fileName); + try (XmlBlock block = new XmlBlock(null, nativeXmlPtr)) { + XmlResourceParser parser = block.newParser(); + // If nativeOpenXml doesn't throw, it will always return a valid native pointer, + // which makes newParser always return non-null. But let's be paranoid. + if (parser == null) { + throw new AssertionError("block.newParser() returned a null parser"); + } + return parser; + } + } + } + + /** + * Returns false if the underlying APK was changed since this ApkAssets was loaded. + */ + public boolean isUpToDate() { + synchronized (this) { + ensureValidLocked(); + return nativeIsUpToDate(mNativePtr); + } + } + + /** + * Closes the ApkAssets and destroys the underlying native implementation. Further use of the + * ApkAssets object will cause exceptions to be thrown. + * + * Calling close on an already closed ApkAssets does nothing. + */ + @Override + public void close() { + synchronized (this) { + if (mNativePtr == 0) { + return; + } + + mStringBlock = null; + nativeDestroy(mNativePtr); + mNativePtr = 0; + } + } + + @Override + protected void finalize() throws Throwable { + if (mNativePtr != 0) { + nativeDestroy(mNativePtr); + } + } + + private void ensureValidLocked() { + if (mNativePtr == 0) { + throw new RuntimeException("ApkAssets is closed"); + } + } + + private static native long nativeLoad( + @NonNull String path, boolean system, boolean forceSharedLib, boolean overlay) + throws IOException; + private static native long nativeLoadFromFd(@NonNull FileDescriptor fd, + @NonNull String friendlyName, boolean system, boolean forceSharedLib) + throws IOException; + private static native void nativeDestroy(long ptr); + private static native @NonNull String nativeGetAssetPath(long ptr); + private static native long nativeGetStringBlock(long ptr); + private static native boolean nativeIsUpToDate(long ptr); + private static native long nativeOpenXml(long ptr, @NonNull String fileName) throws IOException; +} diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java index 78665609bdd..2db5c6b1320 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -18,9 +18,11 @@ package android.content.res; import android.annotation.AnyRes; import android.annotation.ArrayRes; +import android.annotation.AttrRes; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringRes; +import android.annotation.StyleRes; import android.content.pm.ActivityInfo; import android.content.res.Configuration.NativeConfig; import android.os.ParcelFileDescriptor; @@ -28,10 +30,20 @@ import android.util.Log; import android.util.SparseArray; import android.util.TypedValue; -import java.io.FileDescriptor; +import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.Preconditions; + +import libcore.io.IoUtils; + +import java.io.BufferedReader; +import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.channels.FileLock; +import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; /** @@ -42,7 +54,17 @@ import java.util.HashMap; * bytes. */ public final class AssetManager implements AutoCloseable { - /* modes used when opening an asset */ + private static final String TAG = "AssetManager"; + private static final boolean DEBUG_REFS = false; + + private static final String FRAMEWORK_APK_PATH = "/system/framework/framework-res.apk"; + + private static final Object sSync = new Object(); + + // Not private for LayoutLib's BridgeAssetManager. + @GuardedBy("sSync") static AssetManager sSystem = null; + + @GuardedBy("sSync") private static ApkAssets[] sSystemApkAssets = new ApkAssets[0]; /** * Mode for {@link #open(String, int)}: no specific information about how @@ -65,87 +87,300 @@ public final class AssetManager implements AutoCloseable { */ public static final int ACCESS_BUFFER = 3; - private static final String TAG = "AssetManager"; - private static final boolean localLOGV = false || false; - - private static final boolean DEBUG_REFS = false; - - private static final Object sSync = new Object(); - /*package*/ static AssetManager sSystem = null; + @GuardedBy("this") private final TypedValue mValue = new TypedValue(); + @GuardedBy("this") private final long[] mOffsets = new long[2]; - private final TypedValue mValue = new TypedValue(); - private final long[] mOffsets = new long[2]; - - // For communication with native code. - private long mObject; + // Pointer to native implementation, stuffed inside a long. + @GuardedBy("this") private long mObject; + + // The loaded asset paths. + @GuardedBy("this") private ApkAssets[] mApkAssets; + + // Debug/reference counting implementation. + @GuardedBy("this") private boolean mOpen = true; + @GuardedBy("this") private int mNumRefs = 1; + @GuardedBy("this") private HashMap mRefStacks; - private StringBlock mStringBlocks[] = null; - - private int mNumRefs = 1; - private boolean mOpen = true; - private HashMap mRefStacks; - /** * Create a new AssetManager containing only the basic system assets. * Applications will not generally use this method, instead retrieving the * appropriate asset manager with {@link Resources#getAssets}. Not for * use by applications. - * {@hide} + * @hide */ public AssetManager() { - synchronized (this) { - if (DEBUG_REFS) { - mNumRefs = 0; - incRefsLocked(this.hashCode()); - } - init(false); - if (localLOGV) Log.v(TAG, "New asset manager: " + this); - ensureSystemAssets(); + final ApkAssets[] assets; + synchronized (sSync) { + createSystemAssetsInZygoteLocked(); + assets = sSystemApkAssets; } - } - private static void ensureSystemAssets() { - synchronized (sSync) { - if (sSystem == null) { - AssetManager system = new AssetManager(true); - system.makeStringBlocks(null); - sSystem = system; - } + mObject = nativeCreate(); + if (DEBUG_REFS) { + mNumRefs = 0; + incRefsLocked(hashCode()); } + + // Always set the framework resources. + setApkAssets(assets, false /*invalidateCaches*/); } - - private AssetManager(boolean isSystem) { + + /** + * Private constructor that doesn't call ensureSystemAssets. + * Used for the creation of system assets. + */ + @SuppressWarnings("unused") + private AssetManager(boolean sentinel) { + mObject = nativeCreate(); if (DEBUG_REFS) { - synchronized (this) { - mNumRefs = 0; - incRefsLocked(this.hashCode()); + mNumRefs = 0; + incRefsLocked(hashCode()); + } + } + + /** + * This must be called from Zygote so that system assets are shared by all applications. + */ + private static void createSystemAssetsInZygoteLocked() { + if (sSystem != null) { + return; + } + + // Make sure that all IDMAPs are up to date. + nativeVerifySystemIdmaps(); + + try { + ArrayList apkAssets = new ArrayList<>(); + apkAssets.add(ApkAssets.loadFromPath(FRAMEWORK_APK_PATH, true /*system*/)); + loadStaticRuntimeOverlays(apkAssets); + + sSystemApkAssets = apkAssets.toArray(new ApkAssets[apkAssets.size()]); + sSystem = new AssetManager(true /*sentinel*/); + sSystem.setApkAssets(sSystemApkAssets, false /*invalidateCaches*/); + } catch (IOException e) { + throw new IllegalStateException("Failed to create system AssetManager", e); + } + } + + /** + * Loads the static runtime overlays declared in /data/resource-cache/overlays.list. + * Throws an exception if the file is corrupt or if loading the APKs referenced by the file + * fails. Returns quietly if the overlays.list file doesn't exist. + * @param outApkAssets The list to fill with the loaded ApkAssets. + */ + private static void loadStaticRuntimeOverlays(ArrayList outApkAssets) + throws IOException { + final FileInputStream fis; + try { + fis = new FileInputStream("/data/resource-cache/overlays.list"); + } catch (FileNotFoundException e) { + // We might not have any overlays, this is fine. We catch here since ApkAssets + // loading can also fail with the same exception, which we would want to propagate. + Log.i(TAG, "no overlays.list file found"); + return; + } + + try { + // Acquire a lock so that any idmap scanning doesn't impact the current set. + // The order of this try-with-resources block matters. We must release the lock, and + // then close the file streams when exiting the block. + try (final BufferedReader br = new BufferedReader(new InputStreamReader(fis)); + final FileLock flock = fis.getChannel().lock(0, Long.MAX_VALUE, true /*shared*/)) { + for (String line; (line = br.readLine()) != null; ) { + final String idmapPath = line.split(" ")[1]; + outApkAssets.add(ApkAssets.loadOverlayFromPath(idmapPath, true /*system*/)); + } } + } finally { + // When BufferedReader is closed above, FileInputStream is closed as well. But let's be + // paranoid. + IoUtils.closeQuietly(fis); } - init(true); - if (localLOGV) Log.v(TAG, "New asset manager: " + this); } /** * Return a global shared asset manager that provides access to only * system assets (no application assets). - * {@hide} + * @hide */ public static AssetManager getSystem() { - ensureSystemAssets(); - return sSystem; + synchronized (sSync) { + createSystemAssetsInZygoteLocked(); + return sSystem; + } } /** * Close this asset manager. */ + @Override public void close() { - synchronized(this) { - //System.out.println("Release: num=" + mNumRefs - // + ", released=" + mReleased); - if (mOpen) { - mOpen = false; - decRefsLocked(this.hashCode()); + synchronized (this) { + if (!mOpen) { + return; + } + + mOpen = false; + decRefsLocked(hashCode()); + } + } + + /** + * Changes the asset paths in this AssetManager. This replaces the {@link #addAssetPath(String)} + * family of methods. + * + * @param apkAssets The new set of paths. + * @param invalidateCaches Whether to invalidate any caches. This should almost always be true. + * Set this to false if you are appending new resources + * (not new configurations). + * @hide + */ + public void setApkAssets(@NonNull ApkAssets[] apkAssets, boolean invalidateCaches) { + Preconditions.checkNotNull(apkAssets, "apkAssets"); + synchronized (this) { + ensureValidLocked(); + mApkAssets = apkAssets; + nativeSetApkAssets(mObject, apkAssets, invalidateCaches); + if (invalidateCaches) { + // Invalidate all caches. + invalidateCachesLocked(-1); + } + } + } + + /** + * Invalidates the caches in this AssetManager according to the bitmask `diff`. + * + * @param diff The bitmask of changes generated by {@link Configuration#diff(Configuration)}. + * @see ActivityInfo.Config + */ + private void invalidateCachesLocked(int diff) { + // TODO(adamlesinski): Currently there are no caches to invalidate in Java code. + } + + /** + * @hide + */ + public @NonNull ApkAssets[] getApkAssets() { + synchronized (this) { + ensureValidLocked(); + return mApkAssets; + } + } + + /** + * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)} + * @hide + */ + @Deprecated + public int addAssetPath(String path) { + return addAssetPathInternal(path, false /*overlay*/, false /*appAsLib*/); + } + + /** + * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)} + * @hide + */ + @Deprecated + public int addAssetPathAsSharedLibrary(String path) { + return addAssetPathInternal(path, false /*overlay*/, true /*appAsLib*/); + } + + /** + * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)} + * @hide + */ + @Deprecated + public int addOverlayPath(String path) { + return addAssetPathInternal(path, true /*overlay*/, false /*appAsLib*/); + } + + private int addAssetPathInternal(String path, boolean overlay, boolean appAsLib) { + Preconditions.checkNotNull(path, "path"); + synchronized (this) { + ensureOpenLocked(); + final int count = mApkAssets.length; + for (int i = 0; i < count; i++) { + if (mApkAssets[i].getAssetPath().equals(path)) { + return i + 1; + } + } + + final ApkAssets assets; + try { + if (overlay) { + // TODO(b/70343104): This hardcoded path will be removed once + // addAssetPathInternal is deleted. + final String idmapPath = "/data/resource-cache/" + + path.substring(1).replace('/', '@') + + "@idmap"; + assets = ApkAssets.loadOverlayFromPath(idmapPath, false /*system*/); + } else { + assets = ApkAssets.loadFromPath(path, false /*system*/, appAsLib); + } + } catch (IOException e) { + return 0; + } + + final ApkAssets[] newApkAssets = Arrays.copyOf(mApkAssets, count + 1); + newApkAssets[count] = assets; + setApkAssets(newApkAssets, true); + return count + 1; + } + } + + /** + * Ensures that the native implementation has not been destroyed. + * The AssetManager may have been closed, but references to it still exist + * and therefore the native implementation is not destroyed. + */ + private void ensureValidLocked() { + if (mObject == 0) { + throw new RuntimeException("AssetManager has been destroyed"); + } + } + + /** + * Ensures that the AssetManager has not been explicitly closed. If this method passes, + * then this implies that ensureValidLocked() also passes. + */ + private void ensureOpenLocked() { + if (!mOpen) { + throw new RuntimeException("AssetManager has been closed"); + } + } + + /** + * Populates {@code outValue} with the data associated a particular + * resource identifier for the current configuration. + * + * @param resId the resource identifier to load + * @param densityDpi the density bucket for which to load the resource + * @param outValue the typed value in which to put the data + * @param resolveRefs {@code true} to resolve references, {@code false} + * to leave them unresolved + * @return {@code true} if the data was loaded into {@code outValue}, + * {@code false} otherwise + */ + boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue, + boolean resolveRefs) { + Preconditions.checkNotNull(outValue, "outValue"); + synchronized (this) { + ensureValidLocked(); + final int cookie = nativeGetResourceValue( + mObject, resId, (short) densityDpi, outValue, resolveRefs); + if (cookie <= 0) { + return false; } + + // Convert the changing configurations flags populated by native code. + outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( + outValue.changingConfigurations); + + if (outValue.type == TypedValue.TYPE_STRING) { + outValue.string = mApkAssets[cookie - 1].getStringFromPool(outValue.data); + } + return true; } } @@ -156,8 +391,7 @@ public final class AssetManager implements AutoCloseable { * @param resId the resource identifier to load * @return the string value, or {@code null} */ - @Nullable - final CharSequence getResourceText(@StringRes int resId) { + @Nullable CharSequence getResourceText(@StringRes int resId) { synchronized (this) { final TypedValue outValue = mValue; if (getResourceValue(resId, 0, outValue, true)) { @@ -172,15 +406,15 @@ public final class AssetManager implements AutoCloseable { * identifier for the current configuration. * * @param resId the resource identifier to load - * @param bagEntryId + * @param bagEntryId the index into the bag to load * @return the string value, or {@code null} */ - @Nullable - final CharSequence getResourceBagText(@StringRes int resId, int bagEntryId) { + @Nullable CharSequence getResourceBagText(@StringRes int resId, int bagEntryId) { synchronized (this) { + ensureValidLocked(); final TypedValue outValue = mValue; - final int block = loadResourceBagValue(resId, bagEntryId, outValue, true); - if (block < 0) { + final int cookie = nativeGetResourceBagValue(mObject, resId, bagEntryId, outValue); + if (cookie <= 0) { return null; } @@ -189,52 +423,60 @@ public final class AssetManager implements AutoCloseable { outValue.changingConfigurations); if (outValue.type == TypedValue.TYPE_STRING) { - return mStringBlocks[block].get(outValue.data); + return mApkAssets[cookie - 1].getStringFromPool(outValue.data); } return outValue.coerceToString(); } } + int getResourceArraySize(@ArrayRes int resId) { + synchronized (this) { + ensureValidLocked(); + return nativeGetResourceArraySize(mObject, resId); + } + } + /** - * Retrieves the string array associated with a particular resource - * identifier for the current configuration. + * Populates `outData` with array elements of `resId`. `outData` is normally + * used with + * {@link TypedArray}. * - * @param resId the resource identifier of the string array - * @return the string array, or {@code null} + * Each logical element in `outData` is {@link TypedArray#STYLE_NUM_ENTRIES} + * long, + * with the indices of the data representing the type, value, asset cookie, + * resource ID, + * configuration change mask, and density of the element. + * + * @param resId The resource ID of an array resource. + * @param outData The array to populate with data. + * @return The length of the array. + * + * @see TypedArray#STYLE_TYPE + * @see TypedArray#STYLE_DATA + * @see TypedArray#STYLE_ASSET_COOKIE + * @see TypedArray#STYLE_RESOURCE_ID + * @see TypedArray#STYLE_CHANGING_CONFIGURATIONS + * @see TypedArray#STYLE_DENSITY */ - @Nullable - final String[] getResourceStringArray(@ArrayRes int resId) { - return getArrayStringResource(resId); + int getResourceArray(@ArrayRes int resId, @NonNull int[] outData) { + Preconditions.checkNotNull(outData, "outData"); + synchronized (this) { + ensureValidLocked(); + return nativeGetResourceArray(mObject, resId, outData); + } } /** - * Populates {@code outValue} with the data associated a particular - * resource identifier for the current configuration. + * Retrieves the string array associated with a particular resource + * identifier for the current configuration. * - * @param resId the resource identifier to load - * @param densityDpi the density bucket for which to load the resource - * @param outValue the typed value in which to put the data - * @param resolveRefs {@code true} to resolve references, {@code false} - * to leave them unresolved - * @return {@code true} if the data was loaded into {@code outValue}, - * {@code false} otherwise + * @param resId the resource identifier of the string array + * @return the string array, or {@code null} */ - final boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue, - boolean resolveRefs) { + @Nullable String[] getResourceStringArray(@ArrayRes int resId) { synchronized (this) { - final int block = loadResourceValue(resId, (short) densityDpi, outValue, resolveRefs); - if (block < 0) { - return false; - } - - // Convert the changing configurations flags populated by native code. - outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( - outValue.changingConfigurations); - - if (outValue.type == TypedValue.TYPE_STRING) { - outValue.string = mStringBlocks[block].get(outValue.data); - } - return true; + ensureValidLocked(); + return nativeGetResourceStringArray(mObject, resId); } } @@ -244,26 +486,48 @@ public final class AssetManager implements AutoCloseable { * * @param resId the resource id of the string array */ - final @Nullable CharSequence[] getResourceTextArray(@ArrayRes int resId) { + @Nullable CharSequence[] getResourceTextArray(@ArrayRes int resId) { synchronized (this) { - final int[] rawInfoArray = getArrayStringInfo(resId); + ensureValidLocked(); + final int[] rawInfoArray = nativeGetResourceStringArrayInfo(mObject, resId); if (rawInfoArray == null) { return null; } + final int rawInfoArrayLen = rawInfoArray.length; final int infoArrayLen = rawInfoArrayLen / 2; - int block; - int index; final CharSequence[] retArray = new CharSequence[infoArrayLen]; for (int i = 0, j = 0; i < rawInfoArrayLen; i = i + 2, j++) { - block = rawInfoArray[i]; - index = rawInfoArray[i + 1]; - retArray[j] = index >= 0 ? mStringBlocks[block].get(index) : null; + int cookie = rawInfoArray[i]; + int index = rawInfoArray[i + 1]; + retArray[j] = (index >= 0 && cookie > 0) + ? mApkAssets[cookie - 1].getStringFromPool(index) : null; } return retArray; } } + @Nullable int[] getResourceIntArray(@ArrayRes int resId) { + synchronized (this) { + ensureValidLocked(); + return nativeGetResourceIntArray(mObject, resId); + } + } + + /** + * Get the attributes for a style resource. These are the <item> + * elements in + * a <style> resource. + * @param resId The resource ID of the style + * @return An array of attribute IDs. + */ + @AttrRes int[] getStyleAttributes(@StyleRes int resId) { + synchronized (this) { + ensureValidLocked(); + return nativeGetStyleAttributes(mObject, resId); + } + } + /** * Populates {@code outValue} with the data associated with a particular * resource identifier for the current configuration. Resolves theme @@ -277,73 +541,88 @@ public final class AssetManager implements AutoCloseable { * @return {@code true} if the data was loaded into {@code outValue}, * {@code false} otherwise */ - final boolean getThemeValue(long theme, @AnyRes int resId, @NonNull TypedValue outValue, + boolean getThemeValue(long theme, @AnyRes int resId, @NonNull TypedValue outValue, boolean resolveRefs) { - final int block = loadThemeAttributeValue(theme, resId, outValue, resolveRefs); - if (block < 0) { - return false; + Preconditions.checkNotNull(outValue, "outValue"); + synchronized (this) { + ensureValidLocked(); + final int cookie = nativeThemeGetAttributeValue(mObject, theme, resId, outValue, + resolveRefs); + if (cookie <= 0) { + return false; + } + + // Convert the changing configurations flags populated by native code. + outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( + outValue.changingConfigurations); + + if (outValue.type == TypedValue.TYPE_STRING) { + outValue.string = mApkAssets[cookie - 1].getStringFromPool(outValue.data); + } + return true; } + } - // Convert the changing configurations flags populated by native code. - outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( - outValue.changingConfigurations); + void dumpTheme(long theme, int priority, String tag, String prefix) { + synchronized (this) { + ensureValidLocked(); + nativeThemeDump(mObject, theme, priority, tag, prefix); + } + } - if (outValue.type == TypedValue.TYPE_STRING) { - final StringBlock[] blocks = ensureStringBlocks(); - outValue.string = blocks[block].get(outValue.data); + @Nullable String getResourceName(@AnyRes int resId) { + synchronized (this) { + ensureValidLocked(); + return nativeGetResourceName(mObject, resId); } - return true; } - /** - * Ensures the string blocks are loaded. - * - * @return the string blocks - */ - @NonNull - final StringBlock[] ensureStringBlocks() { + @Nullable String getResourcePackageName(@AnyRes int resId) { synchronized (this) { - if (mStringBlocks == null) { - makeStringBlocks(sSystem.mStringBlocks); - } - return mStringBlocks; + ensureValidLocked(); + return nativeGetResourcePackageName(mObject, resId); } } - /*package*/ final void makeStringBlocks(StringBlock[] seed) { - final int seedNum = (seed != null) ? seed.length : 0; - final int num = getStringBlockCount(); - mStringBlocks = new StringBlock[num]; - if (localLOGV) Log.v(TAG, "Making string blocks for " + this - + ": " + num); - for (int i=0; i Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)len; - } - public final void close() throws IOException { - synchronized (AssetManager.this) { - if (mAsset != 0) { - destroyAsset(mAsset); - mAsset = 0; - decRefsLocked(hashCode()); - } - } - } - public final void mark(int readlimit) { - mMarkPos = seekAsset(mAsset, 0, 0); + ensureOpen(); + return nativeAssetReadChar(mAssetNativePtr); } - public final void reset() throws IOException { - seekAsset(mAsset, mMarkPos, -1); - } - public final int read(byte[] b) throws IOException { - return readAsset(mAsset, b, 0, b.length); + + @Override + public final int read(@NonNull byte[] b) throws IOException { + ensureOpen(); + Preconditions.checkNotNull(b, "b"); + return nativeAssetRead(mAssetNativePtr, b, 0, b.length); } - public final int read(byte[] b, int off, int len) throws IOException { - return readAsset(mAsset, b, off, len); + + @Override + public final int read(@NonNull byte[] b, int off, int len) throws IOException { + ensureOpen(); + Preconditions.checkNotNull(b, "b"); + return nativeAssetRead(mAssetNativePtr, b, off, len); } + + @Override public final long skip(long n) throws IOException { - long pos = seekAsset(mAsset, 0, 0); - if ((pos+n) > mLength) { - n = mLength-pos; + ensureOpen(); + long pos = nativeAssetSeek(mAssetNativePtr, 0, 0); + if ((pos + n) > mLength) { + n = mLength - pos; } if (n > 0) { - seekAsset(mAsset, n, 0); + nativeAssetSeek(mAssetNativePtr, n, 0); } return n; } - protected void finalize() throws Throwable - { - close(); + @Override + public final int available() throws IOException { + ensureOpen(); + final long len = nativeAssetGetRemainingLength(mAssetNativePtr); + return len > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) len; } - private long mAsset; - private long mLength; - private long mMarkPos; - } - - /** - * Add an additional set of assets to the asset manager. This can be - * either a directory or ZIP file. Not for use by applications. Returns - * the cookie of the added asset, or 0 on failure. - * {@hide} - */ - public final int addAssetPath(String path) { - return addAssetPathInternal(path, false); - } - - /** - * Add an application assets to the asset manager and loading it as shared library. - * This can be either a directory or ZIP file. Not for use by applications. Returns - * the cookie of the added asset, or 0 on failure. - * {@hide} - */ - public final int addAssetPathAsSharedLibrary(String path) { - return addAssetPathInternal(path, true); - } - - private final int addAssetPathInternal(String path, boolean appAsLib) { - synchronized (this) { - int res = addAssetPathNative(path, appAsLib); - makeStringBlocks(mStringBlocks); - return res; + @Override + public final boolean markSupported() { + return true; } - } - - private native final int addAssetPathNative(String path, boolean appAsLib); - /** - * Add an additional set of assets to the asset manager from an already open - * FileDescriptor. Not for use by applications. - * This does not give full AssetManager functionality for these assets, - * since the origin of the file is not known for purposes of sharing, - * overlay resolution, and other features. However it does allow you - * to do simple access to the contents of the given fd as an apk file. - * Performs a dup of the underlying fd, so you must take care of still closing - * the FileDescriptor yourself (and can do that whenever you want). - * Returns the cookie of the added asset, or 0 on failure. - * {@hide} - */ - public int addAssetFd(FileDescriptor fd, String debugPathName) { - return addAssetFdInternal(fd, debugPathName, false); - } - - private int addAssetFdInternal(FileDescriptor fd, String debugPathName, - boolean appAsLib) { - synchronized (this) { - int res = addAssetFdNative(fd, debugPathName, appAsLib); - makeStringBlocks(mStringBlocks); - return res; + @Override + public final void mark(int readlimit) { + ensureOpen(); + mMarkPos = nativeAssetSeek(mAssetNativePtr, 0, 0); } - } - - private native int addAssetFdNative(FileDescriptor fd, String debugPathName, - boolean appAsLib); - /** - * Add a set of assets to overlay an already added set of assets. - * - * This is only intended for application resources. System wide resources - * are handled before any Java code is executed. - * - * {@hide} - */ - - public final int addOverlayPath(String idmapPath) { - synchronized (this) { - int res = addOverlayPathNative(idmapPath); - makeStringBlocks(mStringBlocks); - return res; + @Override + public final void reset() throws IOException { + ensureOpen(); + nativeAssetSeek(mAssetNativePtr, mMarkPos, -1); } - } - /** - * See addOverlayPath. - * - * {@hide} - */ - public native final int addOverlayPathNative(String idmapPath); + @Override + public final void close() throws IOException { + if (mAssetNativePtr != 0) { + nativeAssetDestroy(mAssetNativePtr); + mAssetNativePtr = 0; - /** - * Add multiple sets of assets to the asset manager at once. See - * {@link #addAssetPath(String)} for more information. Returns array of - * cookies for each added asset with 0 indicating failure, or null if - * the input array of paths is null. - * {@hide} - */ - public final int[] addAssetPaths(String[] paths) { - if (paths == null) { - return null; + synchronized (AssetManager.this) { + decRefsLocked(hashCode()); + } + } } - int[] cookies = new int[paths.length]; - for (int i = 0; i < paths.length; i++) { - cookies[i] = addAssetPath(paths[i]); + @Override + protected void finalize() throws Throwable { + close(); } - return cookies; + private void ensureOpen() { + if (mAssetNativePtr == 0) { + throw new IllegalStateException("AssetInputStream is closed"); + } + } } /** * Determine whether the state in this asset manager is up-to-date with * the files on the filesystem. If false is returned, you need to * instantiate a new AssetManager class to see the new data. - * {@hide} + * @hide */ - public native final boolean isUpToDate(); + public boolean isUpToDate() { + for (ApkAssets apkAssets : getApkAssets()) { + if (!apkAssets.isUpToDate()) { + return false; + } + } + return true; + } /** * Get the locales that this asset manager contains data for. @@ -784,7 +1094,12 @@ public final class AssetManager implements AutoCloseable { * are of the form {@code ll_CC} where {@code ll} is a two letter language code, * and {@code CC} is a two letter country code. */ - public native final String[] getLocales(); + public String[] getLocales() { + synchronized (this) { + ensureValidLocked(); + return nativeGetLocales(mObject, false /*excludeSystem*/); + } + } /** * Same as getLocales(), except that locales that are only provided by the system (i.e. those @@ -794,131 +1109,57 @@ public final class AssetManager implements AutoCloseable { * assets support Cherokee and French, getLocales() would return * [Cherokee, English, French, German], while getNonSystemLocales() would return * [Cherokee, French]. - * {@hide} + * @hide */ - public native final String[] getNonSystemLocales(); - - /** {@hide} */ - public native final Configuration[] getSizeConfigurations(); + public String[] getNonSystemLocales() { + synchronized (this) { + ensureValidLocked(); + return nativeGetLocales(mObject, true /*excludeSystem*/); + } + } /** - * Change the configuation used when retrieving resources. Not for use by - * applications. - * {@hide} + * @hide */ - public native final void setConfiguration(int mcc, int mnc, String locale, - int orientation, int touchscreen, int density, int keyboard, - int keyboardHidden, int navigation, int screenWidth, int screenHeight, - int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp, - int screenLayout, int uiMode, int colorMode, int majorVersion); + Configuration[] getSizeConfigurations() { + synchronized (this) { + ensureValidLocked(); + return nativeGetSizeConfigurations(mObject); + } + } /** - * Retrieve the resource identifier for the given resource name. + * Change the configuration used when retrieving resources. Not for use by + * applications. + * @hide */ - /*package*/ native final int getResourceIdentifier(String name, - String defType, - String defPackage); + public void setConfiguration(int mcc, int mnc, @Nullable String locale, int orientation, + int touchscreen, int density, int keyboard, int keyboardHidden, int navigation, + int screenWidth, int screenHeight, int smallestScreenWidthDp, int screenWidthDp, + int screenHeightDp, int screenLayout, int uiMode, int colorMode, int majorVersion) { + synchronized (this) { + ensureValidLocked(); + nativeSetConfiguration(mObject, mcc, mnc, locale, orientation, touchscreen, density, + keyboard, keyboardHidden, navigation, screenWidth, screenHeight, + smallestScreenWidthDp, screenWidthDp, screenHeightDp, screenLayout, uiMode, + colorMode, majorVersion); + } + } - /*package*/ native final String getResourceName(int resid); - /*package*/ native final String getResourcePackageName(int resid); - /*package*/ native final String getResourceTypeName(int resid); - /*package*/ native final String getResourceEntryName(int resid); - - private native final long openAsset(String fileName, int accessMode); - private final native ParcelFileDescriptor openAssetFd(String fileName, - long[] outOffsets) throws IOException; - private native final long openNonAssetNative(int cookie, String fileName, - int accessMode); - private native ParcelFileDescriptor openNonAssetFdNative(int cookie, - String fileName, long[] outOffsets) throws IOException; - private native final void destroyAsset(long asset); - private native final int readAssetChar(long asset); - private native final int readAsset(long asset, byte[] b, int off, int len); - private native final long seekAsset(long asset, long offset, int whence); - private native final long getAssetLength(long asset); - private native final long getAssetRemainingLength(long asset); - - /** Returns true if the resource was found, filling in mRetStringBlock and - * mRetData. */ - private native final int loadResourceValue(int ident, short density, TypedValue outValue, - boolean resolve); - /** Returns true if the resource was found, filling in mRetStringBlock and - * mRetData. */ - private native final int loadResourceBagValue(int ident, int bagEntryId, TypedValue outValue, - boolean resolve); - /*package*/ static final int STYLE_NUM_ENTRIES = 6; - /*package*/ static final int STYLE_TYPE = 0; - /*package*/ static final int STYLE_DATA = 1; - /*package*/ static final int STYLE_ASSET_COOKIE = 2; - /*package*/ static final int STYLE_RESOURCE_ID = 3; - - /* Offset within typed data array for native changingConfigurations. */ - static final int STYLE_CHANGING_CONFIGURATIONS = 4; - - /*package*/ static final int STYLE_DENSITY = 5; - /*package*/ native static final void applyStyle(long theme, - int defStyleAttr, int defStyleRes, long xmlParser, - int[] inAttrs, int length, long outValuesAddress, long outIndicesAddress); - /*package*/ native static final boolean resolveAttrs(long theme, - int defStyleAttr, int defStyleRes, int[] inValues, - int[] inAttrs, int[] outValues, int[] outIndices); - /*package*/ native final boolean retrieveAttributes( - long xmlParser, int[] inAttrs, int[] outValues, int[] outIndices); - /*package*/ native final int getArraySize(int resource); - /*package*/ native final int retrieveArray(int resource, int[] outValues); - private native final int getStringBlockCount(); - private native final long getNativeStringBlock(int block); - - /** - * {@hide} - */ - public native final String getCookieName(int cookie); - - /** - * {@hide} - */ - public native final SparseArray getAssignedPackageIdentifiers(); - - /** - * {@hide} - */ - public native static final int getGlobalAssetCount(); - - /** - * {@hide} - */ - public native static final String getAssetAllocations(); - /** - * {@hide} + * @hide */ - public native static final int getGlobalAssetManagerCount(); - - private native final long newTheme(); - private native final void deleteTheme(long theme); - /*package*/ native static final void applyThemeStyle(long theme, int styleRes, boolean force); - /*package*/ native static final void copyTheme(long dest, long source); - /*package*/ native static final void clearTheme(long theme); - /*package*/ native static final int loadThemeAttributeValue(long theme, int ident, - TypedValue outValue, - boolean resolve); - /*package*/ native static final void dumpTheme(long theme, int priority, String tag, String prefix); - /*package*/ native static final @NativeConfig int getThemeChangingConfigurations(long theme); - - private native final long openXmlAssetNative(int cookie, String fileName); - - private native final String[] getArrayStringResource(int arrayRes); - private native final int[] getArrayStringInfo(int arrayRes); - /*package*/ native final int[] getArrayIntResource(int arrayRes); - /*package*/ native final int[] getStyleAttributes(int themeRes); - - private native final void init(boolean isSystem); - private native final void destroy(); - - private final void incRefsLocked(long id) { + public SparseArray getAssignedPackageIdentifiers() { + synchronized (this) { + ensureValidLocked(); + return nativeGetAssignedPackageIdentifiers(mObject); + } + } + + private void incRefsLocked(long id) { if (DEBUG_REFS) { if (mRefStacks == null) { - mRefStacks = new HashMap(); + mRefStacks = new HashMap<>(); } RuntimeException ex = new RuntimeException(); ex.fillInStackTrace(); @@ -926,16 +1167,117 @@ public final class AssetManager implements AutoCloseable { } mNumRefs++; } - - private final void decRefsLocked(long id) { + + private void decRefsLocked(long id) { if (DEBUG_REFS && mRefStacks != null) { mRefStacks.remove(id); } mNumRefs--; - //System.out.println("Dec streams: mNumRefs=" + mNumRefs - // + " mReleased=" + mReleased); - if (mNumRefs == 0) { - destroy(); + if (mNumRefs == 0 && mObject != 0) { + nativeDestroy(mObject); + mObject = 0; } } + + // AssetManager setup native methods. + private static native long nativeCreate(); + private static native void nativeDestroy(long ptr); + private static native void nativeSetApkAssets(long ptr, @NonNull ApkAssets[] apkAssets, + boolean invalidateCaches); + private static native void nativeSetConfiguration(long ptr, int mcc, int mnc, + @Nullable String locale, int orientation, int touchscreen, int density, int keyboard, + int keyboardHidden, int navigation, int screenWidth, int screenHeight, + int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp, int screenLayout, + int uiMode, int colorMode, int majorVersion); + private static native @NonNull SparseArray nativeGetAssignedPackageIdentifiers( + long ptr); + + // File native methods. + private static native @Nullable String[] nativeList(long ptr, @NonNull String path) + throws IOException; + private static native long nativeOpenAsset(long ptr, @NonNull String fileName, int accessMode); + private static native @Nullable ParcelFileDescriptor nativeOpenAssetFd(long ptr, + @NonNull String fileName, long[] outOffsets) throws IOException; + private static native long nativeOpenNonAsset(long ptr, int cookie, @NonNull String fileName, + int accessMode); + private static native @Nullable ParcelFileDescriptor nativeOpenNonAssetFd(long ptr, int cookie, + @NonNull String fileName, @NonNull long[] outOffsets) throws IOException; + private static native long nativeOpenXmlAsset(long ptr, int cookie, @NonNull String fileName); + + // Primitive resource native methods. + private static native int nativeGetResourceValue(long ptr, @AnyRes int resId, short density, + @NonNull TypedValue outValue, boolean resolveReferences); + private static native int nativeGetResourceBagValue(long ptr, @AnyRes int resId, int bagEntryId, + @NonNull TypedValue outValue); + + private static native @Nullable @AttrRes int[] nativeGetStyleAttributes(long ptr, + @StyleRes int resId); + private static native @Nullable String[] nativeGetResourceStringArray(long ptr, + @ArrayRes int resId); + private static native @Nullable int[] nativeGetResourceStringArrayInfo(long ptr, + @ArrayRes int resId); + private static native @Nullable int[] nativeGetResourceIntArray(long ptr, @ArrayRes int resId); + private static native int nativeGetResourceArraySize(long ptr, @ArrayRes int resId); + private static native int nativeGetResourceArray(long ptr, @ArrayRes int resId, + @NonNull int[] outValues); + + // Resource name/ID native methods. + private static native @AnyRes int nativeGetResourceIdentifier(long ptr, @NonNull String name, + @Nullable String defType, @Nullable String defPackage); + private static native @Nullable String nativeGetResourceName(long ptr, @AnyRes int resid); + private static native @Nullable String nativeGetResourcePackageName(long ptr, + @AnyRes int resid); + private static native @Nullable String nativeGetResourceTypeName(long ptr, @AnyRes int resid); + private static native @Nullable String nativeGetResourceEntryName(long ptr, @AnyRes int resid); + private static native @Nullable String[] nativeGetLocales(long ptr, boolean excludeSystem); + private static native @Nullable Configuration[] nativeGetSizeConfigurations(long ptr); + + // Style attribute retrieval native methods. + private static native void nativeApplyStyle(long ptr, long themePtr, @AttrRes int defStyleAttr, + @StyleRes int defStyleRes, long xmlParserPtr, @NonNull int[] inAttrs, + long outValuesAddress, long outIndicesAddress); + private static native boolean nativeResolveAttrs(long ptr, long themePtr, + @AttrRes int defStyleAttr, @StyleRes int defStyleRes, @Nullable int[] inValues, + @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices); + private static native boolean nativeRetrieveAttributes(long ptr, long xmlParserPtr, + @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices); + + // Theme related native methods + private static native long nativeThemeCreate(long ptr); + private static native void nativeThemeDestroy(long themePtr); + private static native void nativeThemeApplyStyle(long ptr, long themePtr, @StyleRes int resId, + boolean force); + static native void nativeThemeCopy(long destThemePtr, long sourceThemePtr); + static native void nativeThemeClear(long themePtr); + private static native int nativeThemeGetAttributeValue(long ptr, long themePtr, + @AttrRes int resId, @NonNull TypedValue outValue, boolean resolve); + private static native void nativeThemeDump(long ptr, long themePtr, int priority, String tag, + String prefix); + static native @NativeConfig int nativeThemeGetChangingConfigurations(long themePtr); + + // AssetInputStream related native methods. + private static native void nativeAssetDestroy(long assetPtr); + private static native int nativeAssetReadChar(long assetPtr); + private static native int nativeAssetRead(long assetPtr, byte[] b, int off, int len); + private static native long nativeAssetSeek(long assetPtr, long offset, int whence); + private static native long nativeAssetGetLength(long assetPtr); + private static native long nativeAssetGetRemainingLength(long assetPtr); + + private static native void nativeVerifySystemIdmaps(); + + // Global debug native methods. + /** + * @hide + */ + public static native int getGlobalAssetCount(); + + /** + * @hide + */ + public static native String getAssetAllocations(); + + /** + * @hide + */ + public static native int getGlobalAssetManagerCount(); } diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index e173653cd96..8f58891ed55 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -590,7 +590,7 @@ public class Resources { */ @NonNull public int[] getIntArray(@ArrayRes int id) throws NotFoundException { - int[] res = mResourcesImpl.getAssets().getArrayIntResource(id); + int[] res = mResourcesImpl.getAssets().getResourceIntArray(id); if (res != null) { return res; } @@ -613,13 +613,13 @@ public class Resources { @NonNull public TypedArray obtainTypedArray(@ArrayRes int id) throws NotFoundException { final ResourcesImpl impl = mResourcesImpl; - int len = impl.getAssets().getArraySize(id); + int len = impl.getAssets().getResourceArraySize(id); if (len < 0) { throw new NotFoundException("Array resource ID #0x" + Integer.toHexString(id)); } TypedArray array = TypedArray.obtain(this, len); - array.mLength = impl.getAssets().retrieveArray(id, array.mData); + array.mLength = impl.getAssets().getResourceArray(id, array.mData); array.mIndices[0] = 0; return array; @@ -1789,8 +1789,7 @@ public class Resources { // out the attributes from the XML file (applying type information // contained in the resources and such). XmlBlock.Parser parser = (XmlBlock.Parser)set; - mResourcesImpl.getAssets().retrieveAttributes(parser.mParseState, attrs, - array.mData, array.mIndices); + mResourcesImpl.getAssets().retrieveAttributes(parser, attrs, array.mData, array.mIndices); array.mXml = parser; diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java index 97cb78bc424..2a4b278bdca 100644 --- a/core/java/android/content/res/ResourcesImpl.java +++ b/core/java/android/content/res/ResourcesImpl.java @@ -168,7 +168,6 @@ public class ResourcesImpl { mDisplayAdjustments = displayAdjustments; mConfiguration.setToDefaults(); updateConfiguration(config, metrics, displayAdjustments.getCompatibilityInfo()); - mAssets.ensureStringBlocks(); } public DisplayAdjustments getDisplayAdjustments() { @@ -1274,8 +1273,7 @@ public class ResourcesImpl { void applyStyle(int resId, boolean force) { synchronized (mKey) { - AssetManager.applyThemeStyle(mTheme, resId, force); - + mAssets.applyStyleToTheme(mTheme, resId, force); mThemeResId = resId; mKey.append(resId, force); } @@ -1284,7 +1282,7 @@ public class ResourcesImpl { void setTo(ThemeImpl other) { synchronized (mKey) { synchronized (other.mKey) { - AssetManager.copyTheme(mTheme, other.mTheme); + AssetManager.nativeThemeCopy(mTheme, other.mTheme); mThemeResId = other.mThemeResId; mKey.setTo(other.getKey()); @@ -1307,12 +1305,10 @@ public class ResourcesImpl { // out the attributes from the XML file (applying type information // contained in the resources and such). final XmlBlock.Parser parser = (XmlBlock.Parser) set; - AssetManager.applyStyle(mTheme, defStyleAttr, defStyleRes, - parser != null ? parser.mParseState : 0, - attrs, attrs.length, array.mDataAddress, array.mIndicesAddress); + mAssets.applyStyle(mTheme, defStyleAttr, defStyleRes, parser, attrs, + array.mDataAddress, array.mIndicesAddress); array.mTheme = wrapper; array.mXml = parser; - return array; } } @@ -1329,7 +1325,7 @@ public class ResourcesImpl { } final TypedArray array = TypedArray.obtain(wrapper.getResources(), len); - AssetManager.resolveAttrs(mTheme, 0, 0, values, attrs, array.mData, array.mIndices); + mAssets.resolveAttrs(mTheme, 0, 0, values, attrs, array.mData, array.mIndices); array.mTheme = wrapper; array.mXml = null; return array; @@ -1349,14 +1345,14 @@ public class ResourcesImpl { @Config int getChangingConfigurations() { synchronized (mKey) { final @NativeConfig int nativeChangingConfig = - AssetManager.getThemeChangingConfigurations(mTheme); + AssetManager.nativeThemeGetChangingConfigurations(mTheme); return ActivityInfo.activityInfoConfigNativeToJava(nativeChangingConfig); } } public void dump(int priority, String tag, String prefix) { synchronized (mKey) { - AssetManager.dumpTheme(mTheme, priority, tag, prefix); + mAssets.dumpTheme(mTheme, priority, tag, prefix); } } @@ -1385,13 +1381,13 @@ public class ResourcesImpl { */ void rebase() { synchronized (mKey) { - AssetManager.clearTheme(mTheme); + AssetManager.nativeThemeClear(mTheme); // Reapply the same styles in the same order. for (int i = 0; i < mKey.mCount; i++) { final int resId = mKey.mResId[i]; final boolean force = mKey.mForce[i]; - AssetManager.applyThemeStyle(mTheme, resId, force); + mAssets.applyStyleToTheme(mTheme, resId, force); } } } diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java index f33c75168a5..cbb3c6df055 100644 --- a/core/java/android/content/res/TypedArray.java +++ b/core/java/android/content/res/TypedArray.java @@ -61,6 +61,15 @@ public class TypedArray { return attrs; } + // STYLE_ prefixed constants are offsets within the typed data array. + static final int STYLE_NUM_ENTRIES = 6; + static final int STYLE_TYPE = 0; + static final int STYLE_DATA = 1; + static final int STYLE_ASSET_COOKIE = 2; + static final int STYLE_RESOURCE_ID = 3; + static final int STYLE_CHANGING_CONFIGURATIONS = 4; + static final int STYLE_DENSITY = 5; + private final Resources mResources; private DisplayMetrics mMetrics; private AssetManager mAssets; @@ -78,7 +87,7 @@ public class TypedArray { private void resize(int len) { mLength = len; - final int dataLen = len * AssetManager.STYLE_NUM_ENTRIES; + final int dataLen = len * STYLE_NUM_ENTRIES; final int indicesLen = len + 1; final VMRuntime runtime = VMRuntime.getRuntime(); if (mDataAddress == 0 || mData.length < dataLen) { @@ -166,9 +175,9 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return null; } else if (type == TypedValue.TYPE_STRING) { @@ -203,9 +212,9 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return null; } else if (type == TypedValue.TYPE_STRING) { @@ -242,14 +251,13 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_STRING) { - final int cookie = data[index+AssetManager.STYLE_ASSET_COOKIE]; + final int cookie = data[index + STYLE_ASSET_COOKIE]; if (cookie < 0) { - return mXml.getPooledString( - data[index+AssetManager.STYLE_DATA]).toString(); + return mXml.getPooledString(data[index + STYLE_DATA]).toString(); } } return null; @@ -274,11 +282,11 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; final @Config int changingConfigs = ActivityInfo.activityInfoConfigNativeToJava( - data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]); + data[index + STYLE_CHANGING_CONFIGURATIONS]); if ((changingConfigs & ~allowedChangingConfigs) != 0) { return null; } @@ -320,14 +328,14 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index+AssetManager.STYLE_DATA] != 0; + return data[index + STYLE_DATA] != 0; } final TypedValue v = mValue; @@ -359,14 +367,14 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index+AssetManager.STYLE_DATA]; + return data[index + STYLE_DATA]; } final TypedValue v = mValue; @@ -396,16 +404,16 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_FLOAT) { - return Float.intBitsToFloat(data[index+AssetManager.STYLE_DATA]); + return Float.intBitsToFloat(data[index + STYLE_DATA]); } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index+AssetManager.STYLE_DATA]; + return data[index + STYLE_DATA]; } final TypedValue v = mValue; @@ -446,15 +454,15 @@ public class TypedArray { } final int attrIndex = index; - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index+AssetManager.STYLE_DATA]; + return data[index + STYLE_DATA]; } else if (type == TypedValue.TYPE_STRING) { final TypedValue value = mValue; if (getValueAt(index, value)) { @@ -498,7 +506,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { if (value.type == TypedValue.TYPE_ATTRIBUTE) { throw new UnsupportedOperationException( "Failed to resolve attribute at index " + index + ": " + value); @@ -533,7 +541,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { if (value.type == TypedValue.TYPE_ATTRIBUTE) { throw new UnsupportedOperationException( "Failed to resolve attribute at index " + index + ": " + value); @@ -564,15 +572,15 @@ public class TypedArray { } final int attrIndex = index; - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index+AssetManager.STYLE_DATA]; + return data[index + STYLE_DATA]; } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -612,15 +620,14 @@ public class TypedArray { } final int attrIndex = index; - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimension( - data[index + AssetManager.STYLE_DATA], mMetrics); + return TypedValue.complexToDimension(data[index + STYLE_DATA], mMetrics); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -661,15 +668,14 @@ public class TypedArray { } final int attrIndex = index; - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelOffset( - data[index + AssetManager.STYLE_DATA], mMetrics); + return TypedValue.complexToDimensionPixelOffset(data[index + STYLE_DATA], mMetrics); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -711,15 +717,14 @@ public class TypedArray { } final int attrIndex = index; - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelSize( - data[index+AssetManager.STYLE_DATA], mMetrics); + return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -755,16 +760,15 @@ public class TypedArray { } final int attrIndex = index; - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index+AssetManager.STYLE_DATA]; + return data[index + STYLE_DATA]; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelSize( - data[index+AssetManager.STYLE_DATA], mMetrics); + return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -795,15 +799,14 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index+AssetManager.STYLE_DATA]; + return data[index + STYLE_DATA]; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelSize( - data[index + AssetManager.STYLE_DATA], mMetrics); + return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics); } return defValue; @@ -833,15 +836,14 @@ public class TypedArray { } final int attrIndex = index; - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_FRACTION) { - return TypedValue.complexToFraction( - data[index+AssetManager.STYLE_DATA], base, pbase); + return TypedValue.complexToFraction(data[index + STYLE_DATA], base, pbase); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -874,10 +876,10 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - if (data[index+AssetManager.STYLE_TYPE] != TypedValue.TYPE_NULL) { - final int resid = data[index+AssetManager.STYLE_RESOURCE_ID]; + if (data[index + STYLE_TYPE] != TypedValue.TYPE_NULL) { + final int resid = data[index + STYLE_RESOURCE_ID]; if (resid != 0) { return resid; } @@ -902,10 +904,10 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - if (data[index + AssetManager.STYLE_TYPE] == TypedValue.TYPE_ATTRIBUTE) { - return data[index + AssetManager.STYLE_DATA]; + if (data[index + STYLE_TYPE] == TypedValue.TYPE_ATTRIBUTE) { + return data[index + STYLE_DATA]; } return defValue; } @@ -939,7 +941,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { if (value.type == TypedValue.TYPE_ATTRIBUTE) { throw new UnsupportedOperationException( "Failed to resolve attribute at index " + index + ": " + value); @@ -975,7 +977,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { if (value.type == TypedValue.TYPE_ATTRIBUTE) { throw new UnsupportedOperationException( "Failed to resolve attribute at index " + index + ": " + value); @@ -1006,7 +1008,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { return mResources.getTextArray(value.resourceId); } return null; @@ -1027,7 +1029,7 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - return getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, outValue); + return getValueAt(index * STYLE_NUM_ENTRIES, outValue); } /** @@ -1043,8 +1045,8 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; - return mData[index + AssetManager.STYLE_TYPE]; + index *= STYLE_NUM_ENTRIES; + return mData[index + STYLE_TYPE]; } /** @@ -1063,9 +1065,9 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; return type != TypedValue.TYPE_NULL; } @@ -1084,11 +1086,11 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; return type != TypedValue.TYPE_NULL - || data[index+AssetManager.STYLE_DATA] == TypedValue.DATA_NULL_EMPTY; + || data[index + STYLE_DATA] == TypedValue.DATA_NULL_EMPTY; } /** @@ -1109,7 +1111,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { return value; } return null; @@ -1181,16 +1183,16 @@ public class TypedArray { final int[] data = mData; final int N = length(); for (int i = 0; i < N; i++) { - final int index = i * AssetManager.STYLE_NUM_ENTRIES; - if (data[index + AssetManager.STYLE_TYPE] != TypedValue.TYPE_ATTRIBUTE) { + final int index = i * STYLE_NUM_ENTRIES; + if (data[index + STYLE_TYPE] != TypedValue.TYPE_ATTRIBUTE) { // Not an attribute, ignore. continue; } // Null the entry so that we can safely call getZzz(). - data[index + AssetManager.STYLE_TYPE] = TypedValue.TYPE_NULL; + data[index + STYLE_TYPE] = TypedValue.TYPE_NULL; - final int attr = data[index + AssetManager.STYLE_DATA]; + final int attr = data[index + STYLE_DATA]; if (attr == 0) { // Useless data, ignore. continue; @@ -1231,45 +1233,44 @@ public class TypedArray { final int[] data = mData; final int N = length(); for (int i = 0; i < N; i++) { - final int index = i * AssetManager.STYLE_NUM_ENTRIES; - final int type = data[index + AssetManager.STYLE_TYPE]; + final int index = i * STYLE_NUM_ENTRIES; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { continue; } changingConfig |= ActivityInfo.activityInfoConfigNativeToJava( - data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]); + data[index + STYLE_CHANGING_CONFIGURATIONS]); } return changingConfig; } private boolean getValueAt(int index, TypedValue outValue) { final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return false; } outValue.type = type; - outValue.data = data[index+AssetManager.STYLE_DATA]; - outValue.assetCookie = data[index+AssetManager.STYLE_ASSET_COOKIE]; - outValue.resourceId = data[index+AssetManager.STYLE_RESOURCE_ID]; + outValue.data = data[index + STYLE_DATA]; + outValue.assetCookie = data[index + STYLE_ASSET_COOKIE]; + outValue.resourceId = data[index + STYLE_RESOURCE_ID]; outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( - data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]); - outValue.density = data[index+AssetManager.STYLE_DENSITY]; + data[index + STYLE_CHANGING_CONFIGURATIONS]); + outValue.density = data[index + STYLE_DENSITY]; outValue.string = (type == TypedValue.TYPE_STRING) ? loadStringValueAt(index) : null; return true; } private CharSequence loadStringValueAt(int index) { final int[] data = mData; - final int cookie = data[index+AssetManager.STYLE_ASSET_COOKIE]; + final int cookie = data[index + STYLE_ASSET_COOKIE]; if (cookie < 0) { if (mXml != null) { - return mXml.getPooledString( - data[index+AssetManager.STYLE_DATA]); + return mXml.getPooledString(data[index + STYLE_DATA]); } return null; } - return mAssets.getPooledStringForCookie(cookie, data[index+AssetManager.STYLE_DATA]); + return mAssets.getPooledStringForCookie(cookie, data[index + STYLE_DATA]); } /** @hide */ diff --git a/core/java/android/content/res/XmlBlock.java b/core/java/android/content/res/XmlBlock.java index e6b957414ea..d4ccffb83ca 100644 --- a/core/java/android/content/res/XmlBlock.java +++ b/core/java/android/content/res/XmlBlock.java @@ -16,6 +16,7 @@ package android.content.res; +import android.annotation.Nullable; import android.util.TypedValue; import com.android.internal.util.XmlUtils; @@ -33,7 +34,7 @@ import java.io.Reader; * * {@hide} */ -final class XmlBlock { +final class XmlBlock implements AutoCloseable { private static final boolean DEBUG=false; public XmlBlock(byte[] data) { @@ -48,6 +49,7 @@ final class XmlBlock { mStrings = new StringBlock(nativeGetStringBlock(mNative), false); } + @Override public void close() { synchronized (this) { if (mOpen) { @@ -478,13 +480,13 @@ final class XmlBlock { * are doing! The given native object must exist for the entire lifetime * of this newly creating XmlBlock. */ - XmlBlock(AssetManager assets, long xmlBlock) { + XmlBlock(@Nullable AssetManager assets, long xmlBlock) { mAssets = assets; mNative = xmlBlock; mStrings = new StringBlock(nativeGetStringBlock(xmlBlock), false); } - private final AssetManager mAssets; + private @Nullable final AssetManager mAssets; private final long mNative; /*package*/ final StringBlock mStrings; private boolean mOpen = true; diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 33f80ce8ffb..78a3e137ccb 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -110,8 +110,8 @@ cc_library_shared { "android_util_AssetManager.cpp", "android_util_Binder.cpp", "android_util_EventLog.cpp", - "android_util_MemoryIntArray.cpp", "android_util_Log.cpp", + "android_util_MemoryIntArray.cpp", "android_util_PathParser.cpp", "android_util_Process.cpp", "android_util_StringBlock.cpp", @@ -191,6 +191,7 @@ cc_library_shared { "android_backup_FileBackupHelperBase.cpp", "android_backup_BackupHelperDispatcher.cpp", "android_app_backup_FullBackup.cpp", + "android_content_res_ApkAssets.cpp", "android_content_res_ObbScanner.cpp", "android_content_res_Configuration.cpp", "android_animation_PropertyValuesHolder.cpp", diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index d20217386b1..4a032c4bbae 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -123,6 +123,7 @@ extern int register_android_util_MemoryIntArray(JNIEnv* env); extern int register_android_util_PathParser(JNIEnv* env); extern int register_android_content_StringBlock(JNIEnv* env); extern int register_android_content_XmlBlock(JNIEnv* env); +extern int register_android_content_res_ApkAssets(JNIEnv* env); extern int register_android_graphics_Canvas(JNIEnv* env); extern int register_android_graphics_CanvasProperty(JNIEnv* env); extern int register_android_graphics_ColorFilter(JNIEnv* env); @@ -1346,6 +1347,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_content_AssetManager), REG_JNI(register_android_content_StringBlock), REG_JNI(register_android_content_XmlBlock), + REG_JNI(register_android_content_res_ApkAssets), REG_JNI(register_android_text_AndroidCharacter), REG_JNI(register_android_text_Hyphenator), REG_JNI(register_android_text_MeasuredParagraph), diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp index 937b3ffb9d6..f6223fad3dc 100644 --- a/core/jni/android/graphics/FontFamily.cpp +++ b/core/jni/android/graphics/FontFamily.cpp @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include "Utils.h" #include "FontUtils.h" @@ -217,7 +217,8 @@ static jboolean FontFamily_addFontFromAssetManager(JNIEnv* env, jobject, jlong b NPE_CHECK_RETURN_ZERO(env, jpath); NativeFamilyBuilder* builder = reinterpret_cast(builderPtr); - AssetManager* mgr = assetManagerForJavaObject(env, jassetMgr); + + Guarded* mgr = AssetManagerForJavaObject(env, jassetMgr); if (NULL == mgr) { builder->axes.clear(); return false; @@ -229,27 +230,33 @@ static jboolean FontFamily_addFontFromAssetManager(JNIEnv* env, jobject, jlong b return false; } - Asset* asset; - if (isAsset) { - asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER); - } else { - asset = cookie ? mgr->openNonAsset(static_cast(cookie), str.c_str(), - Asset::ACCESS_BUFFER) : mgr->openNonAsset(str.c_str(), Asset::ACCESS_BUFFER); + std::unique_ptr asset; + { + ScopedLock locked_mgr(*mgr); + if (isAsset) { + asset = locked_mgr->Open(str.c_str(), Asset::ACCESS_BUFFER); + } else if (cookie > 0) { + // Valid java cookies are 1-based, but AssetManager cookies are 0-based. + asset = locked_mgr->OpenNonAsset(str.c_str(), static_cast(cookie - 1), + Asset::ACCESS_BUFFER); + } else { + asset = locked_mgr->OpenNonAsset(str.c_str(), Asset::ACCESS_BUFFER); + } } - if (NULL == asset) { + if (nullptr == asset) { builder->axes.clear(); return false; } const void* buf = asset->getBuffer(false); if (NULL == buf) { - delete asset; builder->axes.clear(); return false; } - sk_sp data(SkData::MakeWithProc(buf, asset->getLength(), releaseAsset, asset)); + sk_sp data(SkData::MakeWithProc(buf, asset->getLength(), releaseAsset, + asset.release())); return addSkTypeface(builder, std::move(data), ttcIndex, weight, isItalic); } diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp index 09e37e1a3de..49a24a30f77 100644 --- a/core/jni/android_app_NativeActivity.cpp +++ b/core/jni/android_app_NativeActivity.cpp @@ -361,7 +361,7 @@ loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jstring funcName code->sdkVersion = sdkVersion; code->javaAssetManager = env->NewGlobalRef(jAssetMgr); - code->assetManager = assetManagerForJavaObject(env, jAssetMgr); + code->assetManager = NdkAssetManagerForJavaObject(env, jAssetMgr); if (obbDir != NULL) { dirStr = env->GetStringUTFChars(obbDir, NULL); diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp new file mode 100644 index 00000000000..c0f151b71c9 --- /dev/null +++ b/core/jni/android_content_res_ApkAssets.cpp @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2017 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 "android-base/macros.h" +#include "android-base/stringprintf.h" +#include "android-base/unique_fd.h" +#include "androidfw/ApkAssets.h" +#include "utils/misc.h" + +#include "core_jni_helpers.h" +#include "jni.h" +#include "nativehelper/ScopedUtfChars.h" + +using ::android::base::unique_fd; + +namespace android { + +static jlong NativeLoad(JNIEnv* env, jclass /*clazz*/, jstring java_path, jboolean system, + jboolean force_shared_lib, jboolean overlay) { + ScopedUtfChars path(env, java_path); + if (path.c_str() == nullptr) { + return 0; + } + + std::unique_ptr apk_assets; + if (overlay) { + apk_assets = ApkAssets::LoadOverlay(path.c_str(), system); + } else if (force_shared_lib) { + apk_assets = ApkAssets::LoadAsSharedLibrary(path.c_str(), system); + } else { + apk_assets = ApkAssets::Load(path.c_str(), system); + } + + if (apk_assets == nullptr) { + std::string error_msg = base::StringPrintf("Failed to load asset path %s", path.c_str()); + jniThrowException(env, "java/io/IOException", error_msg.c_str()); + return 0; + } + return reinterpret_cast(apk_assets.release()); +} + +static jlong NativeLoadFromFd(JNIEnv* env, jclass /*clazz*/, jobject file_descriptor, + jstring friendly_name, jboolean system, jboolean force_shared_lib) { + ScopedUtfChars friendly_name_utf8(env, friendly_name); + if (friendly_name_utf8.c_str() == nullptr) { + return 0; + } + + int fd = jniGetFDFromFileDescriptor(env, file_descriptor); + if (fd < 0) { + jniThrowException(env, "java/lang/IllegalArgumentException", "Bad FileDescriptor"); + return 0; + } + + unique_fd dup_fd(::dup(fd)); + if (dup_fd < 0) { + jniThrowIOException(env, errno); + return 0; + } + + std::unique_ptr apk_assets = ApkAssets::LoadFromFd(std::move(dup_fd), + friendly_name_utf8.c_str(), + system, force_shared_lib); + if (apk_assets == nullptr) { + std::string error_msg = base::StringPrintf("Failed to load asset path %s from fd %d", + friendly_name_utf8.c_str(), dup_fd.get()); + jniThrowException(env, "java/io/IOException", error_msg.c_str()); + return 0; + } + return reinterpret_cast(apk_assets.release()); +} + +static void NativeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { + delete reinterpret_cast(ptr); +} + +static jstring NativeGetAssetPath(JNIEnv* env, jclass /*clazz*/, jlong ptr) { + const ApkAssets* apk_assets = reinterpret_cast(ptr); + return env->NewStringUTF(apk_assets->GetPath().c_str()); +} + +static jlong NativeGetStringBlock(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { + const ApkAssets* apk_assets = reinterpret_cast(ptr); + return reinterpret_cast(apk_assets->GetLoadedArsc()->GetStringPool()); +} + +static jboolean NativeIsUpToDate(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { + const ApkAssets* apk_assets = reinterpret_cast(ptr); + (void)apk_assets; + return JNI_TRUE; +} + +static jlong NativeOpenXml(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring file_name) { + ScopedUtfChars path_utf8(env, file_name); + if (path_utf8.c_str() == nullptr) { + return 0; + } + + const ApkAssets* apk_assets = reinterpret_cast(ptr); + std::unique_ptr asset = apk_assets->Open(path_utf8.c_str(), + Asset::AccessMode::ACCESS_RANDOM); + if (asset == nullptr) { + jniThrowException(env, "java/io/FileNotFoundException", path_utf8.c_str()); + return 0; + } + + // DynamicRefTable is only needed when looking up resource references. Opening an XML file + // directly from an ApkAssets has no notion of proper resource references. + std::unique_ptr xml_tree = util::make_unique(nullptr /*dynamicRefTable*/); + status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true); + asset.reset(); + + if (err != NO_ERROR) { + jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file"); + return 0; + } + return reinterpret_cast(xml_tree.release()); +} + +// JNI registration. +static const JNINativeMethod gApkAssetsMethods[] = { + {"nativeLoad", "(Ljava/lang/String;ZZZ)J", (void*)NativeLoad}, + {"nativeLoadFromFd", "(Ljava/io/FileDescriptor;Ljava/lang/String;ZZ)J", + (void*)NativeLoadFromFd}, + {"nativeDestroy", "(J)V", (void*)NativeDestroy}, + {"nativeGetAssetPath", "(J)Ljava/lang/String;", (void*)NativeGetAssetPath}, + {"nativeGetStringBlock", "(J)J", (void*)NativeGetStringBlock}, + {"nativeIsUpToDate", "(J)Z", (void*)NativeIsUpToDate}, + {"nativeOpenXml", "(JLjava/lang/String;)J", (void*)NativeOpenXml}, +}; + +int register_android_content_res_ApkAssets(JNIEnv* env) { + return RegisterMethodsOrDie(env, "android/content/res/ApkAssets", gApkAssetsMethods, + arraysize(gApkAssetsMethods)); +} + +} // namespace android diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index 683b4c490ec..c623ca62129 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -1,1851 +1,1441 @@ -/* //device/libs/android_runtime/android_util_AssetManager.cpp -** -** Copyright 2006, 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. -*/ +/* + * Copyright 2006, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #define LOG_TAG "asset" -#include - #include #include #include -#include -#include #include #include +#include +#include #include // for AID_SYSTEM +#include "android-base/logging.h" +#include "android-base/properties.h" +#include "android-base/stringprintf.h" +#include "android_runtime/android_util_AssetManager.h" +#include "android_runtime/AndroidRuntime.h" +#include "android_util_Binder.h" #include "androidfw/Asset.h" #include "androidfw/AssetManager.h" +#include "androidfw/AssetManager2.h" #include "androidfw/AttributeResolution.h" +#include "androidfw/MutexGuard.h" #include "androidfw/ResourceTypes.h" -#include "android_runtime/AndroidRuntime.h" -#include "android_util_Binder.h" #include "core_jni_helpers.h" #include "jni.h" -#include -#include -#include +#include "nativehelper/JNIHelp.h" +#include "nativehelper/ScopedPrimitiveArray.h" +#include "nativehelper/ScopedStringChars.h" +#include "nativehelper/ScopedUtfChars.h" #include "utils/Log.h" -#include "utils/misc.h" #include "utils/String8.h" +#include "utils/misc.h" extern "C" int capget(cap_user_header_t hdrp, cap_user_data_t datap); extern "C" int capset(cap_user_header_t hdrp, const cap_user_data_t datap); +using ::android::base::StringPrintf; namespace android { -static const bool kThrowOnBadId = false; - // ---------------------------------------------------------------------------- -static struct typedvalue_offsets_t -{ - jfieldID mType; - jfieldID mData; - jfieldID mString; - jfieldID mAssetCookie; - jfieldID mResourceId; - jfieldID mChangingConfigurations; - jfieldID mDensity; +static struct typedvalue_offsets_t { + jfieldID mType; + jfieldID mData; + jfieldID mString; + jfieldID mAssetCookie; + jfieldID mResourceId; + jfieldID mChangingConfigurations; + jfieldID mDensity; } gTypedValueOffsets; -static struct assetfiledescriptor_offsets_t -{ - jfieldID mFd; - jfieldID mStartOffset; - jfieldID mLength; +static struct assetfiledescriptor_offsets_t { + jfieldID mFd; + jfieldID mStartOffset; + jfieldID mLength; } gAssetFileDescriptorOffsets; -static struct assetmanager_offsets_t -{ - jfieldID mObject; +static struct assetmanager_offsets_t { + jfieldID mObject; } gAssetManagerOffsets; -static struct sparsearray_offsets_t -{ - jclass classObject; - jmethodID constructor; - jmethodID put; +static struct { + jfieldID native_ptr; +} gApkAssetsFields; + +static struct sparsearray_offsets_t { + jclass classObject; + jmethodID constructor; + jmethodID put; } gSparseArrayOffsets; -static struct configuration_offsets_t -{ - jclass classObject; - jmethodID constructor; - jfieldID mSmallestScreenWidthDpOffset; - jfieldID mScreenWidthDpOffset; - jfieldID mScreenHeightDpOffset; +static struct configuration_offsets_t { + jclass classObject; + jmethodID constructor; + jfieldID mSmallestScreenWidthDpOffset; + jfieldID mScreenWidthDpOffset; + jfieldID mScreenHeightDpOffset; } gConfigurationOffsets; -jclass g_stringClass = NULL; +jclass g_stringClass = nullptr; // ---------------------------------------------------------------------------- -static jint copyValue(JNIEnv* env, jobject outValue, const ResTable* table, - const Res_value& value, uint32_t ref, ssize_t block, - uint32_t typeSpecFlags, ResTable_config* config = NULL); - -jint copyValue(JNIEnv* env, jobject outValue, const ResTable* table, - const Res_value& value, uint32_t ref, ssize_t block, - uint32_t typeSpecFlags, ResTable_config* config) -{ - env->SetIntField(outValue, gTypedValueOffsets.mType, value.dataType); - env->SetIntField(outValue, gTypedValueOffsets.mAssetCookie, - static_cast(table->getTableCookie(block))); - env->SetIntField(outValue, gTypedValueOffsets.mData, value.data); - env->SetObjectField(outValue, gTypedValueOffsets.mString, NULL); - env->SetIntField(outValue, gTypedValueOffsets.mResourceId, ref); - env->SetIntField(outValue, gTypedValueOffsets.mChangingConfigurations, - typeSpecFlags); - if (config != NULL) { - env->SetIntField(outValue, gTypedValueOffsets.mDensity, config->density); - } - return block; +// Java asset cookies have 0 as an invalid cookie, but TypedArray expects < 0. +constexpr inline static jint ApkAssetsCookieToJavaCookie(ApkAssetsCookie cookie) { + return cookie != kInvalidCookie ? static_cast(cookie + 1) : -1; } -// This is called by zygote (running as user root) as part of preloadResources. -static void verifySystemIdmaps() -{ - pid_t pid; - char system_id[10]; - - snprintf(system_id, sizeof(system_id), "%d", AID_SYSTEM); - - switch (pid = fork()) { - case -1: - ALOGE("failed to fork for idmap: %s", strerror(errno)); - break; - case 0: // child - { - struct __user_cap_header_struct capheader; - struct __user_cap_data_struct capdata; - - memset(&capheader, 0, sizeof(capheader)); - memset(&capdata, 0, sizeof(capdata)); - - capheader.version = _LINUX_CAPABILITY_VERSION; - capheader.pid = 0; - - if (capget(&capheader, &capdata) != 0) { - ALOGE("capget: %s\n", strerror(errno)); - exit(1); - } - - capdata.effective = capdata.permitted; - if (capset(&capheader, &capdata) != 0) { - ALOGE("capset: %s\n", strerror(errno)); - exit(1); - } - - if (setgid(AID_SYSTEM) != 0) { - ALOGE("setgid: %s\n", strerror(errno)); - exit(1); - } - - if (setuid(AID_SYSTEM) != 0) { - ALOGE("setuid: %s\n", strerror(errno)); - exit(1); - } - - // Generic idmap parameters - const char* argv[8]; - int argc = 0; - struct stat st; - - memset(argv, NULL, sizeof(argv)); - argv[argc++] = AssetManager::IDMAP_BIN; - argv[argc++] = "--scan"; - argv[argc++] = AssetManager::TARGET_PACKAGE_NAME; - argv[argc++] = AssetManager::TARGET_APK_PATH; - argv[argc++] = AssetManager::IDMAP_DIR; - - // Directories to scan for overlays: if OVERLAY_THEME_DIR_PROPERTY is defined, - // use OVERLAY_DIR/ in addition to OVERLAY_DIR. - char subdir[PROP_VALUE_MAX]; - int len = __system_property_get(AssetManager::OVERLAY_THEME_DIR_PROPERTY, subdir); - if (len > 0) { - String8 overlayPath = String8(AssetManager::OVERLAY_DIR) + "/" + subdir; - if (stat(overlayPath.string(), &st) == 0) { - argv[argc++] = overlayPath.string(); - } - } - if (stat(AssetManager::OVERLAY_DIR, &st) == 0) { - argv[argc++] = AssetManager::OVERLAY_DIR; - } - - if (stat(AssetManager::PRODUCT_OVERLAY_DIR, &st) == 0) { - argv[argc++] = AssetManager::PRODUCT_OVERLAY_DIR; - } - - // Finally, invoke idmap (if any overlay directory exists) - if (argc > 5) { - execv(AssetManager::IDMAP_BIN, (char* const*)argv); - ALOGE("failed to execv for idmap: %s", strerror(errno)); - exit(1); // should never get here - } else { - exit(0); - } - } - break; - default: // parent - waitpid(pid, NULL, 0); - break; - } +constexpr inline static ApkAssetsCookie JavaCookieToApkAssetsCookie(jint cookie) { + return cookie > 0 ? static_cast(cookie - 1) : kInvalidCookie; } - -// ---------------------------------------------------------------------------- - -// this guy is exported to other jni routines -AssetManager* assetManagerForJavaObject(JNIEnv* env, jobject obj) -{ - jlong amHandle = env->GetLongField(obj, gAssetManagerOffsets.mObject); - AssetManager* am = reinterpret_cast(amHandle); - if (am != NULL) { - return am; - } - jniThrowException(env, "java/lang/IllegalStateException", "AssetManager has been finalized!"); - return NULL; +// This is called by zygote (running as user root) as part of preloadResources. +static void NativeVerifySystemIdmaps(JNIEnv* /*env*/, jclass /*clazz*/) { + switch (pid_t pid = fork()) { + case -1: + PLOG(ERROR) << "failed to fork for idmap"; + break; + + // child + case 0: { + struct __user_cap_header_struct capheader; + struct __user_cap_data_struct capdata; + + memset(&capheader, 0, sizeof(capheader)); + memset(&capdata, 0, sizeof(capdata)); + + capheader.version = _LINUX_CAPABILITY_VERSION; + capheader.pid = 0; + + if (capget(&capheader, &capdata) != 0) { + PLOG(ERROR) << "capget"; + exit(1); + } + + capdata.effective = capdata.permitted; + if (capset(&capheader, &capdata) != 0) { + PLOG(ERROR) << "capset"; + exit(1); + } + + if (setgid(AID_SYSTEM) != 0) { + PLOG(ERROR) << "setgid"; + exit(1); + } + + if (setuid(AID_SYSTEM) != 0) { + PLOG(ERROR) << "setuid"; + exit(1); + } + + // Generic idmap parameters + const char* argv[8]; + int argc = 0; + struct stat st; + + memset(argv, 0, sizeof(argv)); + argv[argc++] = AssetManager::IDMAP_BIN; + argv[argc++] = "--scan"; + argv[argc++] = AssetManager::TARGET_PACKAGE_NAME; + argv[argc++] = AssetManager::TARGET_APK_PATH; + argv[argc++] = AssetManager::IDMAP_DIR; + + // Directories to scan for overlays: if OVERLAY_THEME_DIR_PROPERTY is defined, + // use OVERLAY_DIR/ in addition to OVERLAY_DIR. + std::string overlay_theme_path = base::GetProperty(AssetManager::OVERLAY_THEME_DIR_PROPERTY, + ""); + if (!overlay_theme_path.empty()) { + overlay_theme_path = std::string(AssetManager::OVERLAY_DIR) + "/" + overlay_theme_path; + if (stat(overlay_theme_path.c_str(), &st) == 0) { + argv[argc++] = overlay_theme_path.c_str(); + } + } + + if (stat(AssetManager::OVERLAY_DIR, &st) == 0) { + argv[argc++] = AssetManager::OVERLAY_DIR; + } + + if (stat(AssetManager::PRODUCT_OVERLAY_DIR, &st) == 0) { + argv[argc++] = AssetManager::PRODUCT_OVERLAY_DIR; + } + + // Finally, invoke idmap (if any overlay directory exists) + if (argc > 5) { + execv(AssetManager::IDMAP_BIN, (char* const*)argv); + PLOG(ERROR) << "failed to execv for idmap"; + exit(1); // should never get here + } else { + exit(0); + } + } break; + + // parent + default: + waitpid(pid, nullptr, 0); + break; + } } -static jlong android_content_AssetManager_openAsset(JNIEnv* env, jobject clazz, - jstring fileName, jint mode) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - - ALOGV("openAsset in %p (Java object %p)\n", am, clazz); - - ScopedUtfChars fileName8(env, fileName); - if (fileName8.c_str() == NULL) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Empty file name"); - return -1; - } - - if (mode != Asset::ACCESS_UNKNOWN && mode != Asset::ACCESS_RANDOM - && mode != Asset::ACCESS_STREAMING && mode != Asset::ACCESS_BUFFER) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode"); - return -1; - } - - Asset* a = am->open(fileName8.c_str(), (Asset::AccessMode)mode); - - if (a == NULL) { - jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); - return -1; - } - - //printf("Created Asset Stream: %p\n", a); - - return reinterpret_cast(a); +static jint CopyValue(JNIEnv* env, ApkAssetsCookie cookie, const Res_value& value, uint32_t ref, + uint32_t type_spec_flags, ResTable_config* config, jobject out_typed_value) { + env->SetIntField(out_typed_value, gTypedValueOffsets.mType, value.dataType); + env->SetIntField(out_typed_value, gTypedValueOffsets.mAssetCookie, + ApkAssetsCookieToJavaCookie(cookie)); + env->SetIntField(out_typed_value, gTypedValueOffsets.mData, value.data); + env->SetObjectField(out_typed_value, gTypedValueOffsets.mString, nullptr); + env->SetIntField(out_typed_value, gTypedValueOffsets.mResourceId, ref); + env->SetIntField(out_typed_value, gTypedValueOffsets.mChangingConfigurations, type_spec_flags); + if (config != nullptr) { + env->SetIntField(out_typed_value, gTypedValueOffsets.mDensity, config->density); + } + return static_cast(ApkAssetsCookieToJavaCookie(cookie)); } -static jobject returnParcelFileDescriptor(JNIEnv* env, Asset* a, jlongArray outOffsets) -{ - off64_t startOffset, length; - int fd = a->openFileDescriptor(&startOffset, &length); - delete a; - - if (fd < 0) { - jniThrowException(env, "java/io/FileNotFoundException", - "This file can not be opened as a file descriptor; it is probably compressed"); - return NULL; - } - - jlong* offsets = (jlong*)env->GetPrimitiveArrayCritical(outOffsets, 0); - if (offsets == NULL) { - close(fd); - return NULL; - } - - offsets[0] = startOffset; - offsets[1] = length; - - env->ReleasePrimitiveArrayCritical(outOffsets, offsets, 0); +// ---------------------------------------------------------------------------- - jobject fileDesc = jniCreateFileDescriptor(env, fd); - if (fileDesc == NULL) { - close(fd); - return NULL; - } +// Let the opaque type AAssetManager refer to a guarded AssetManager2 instance. +struct GuardedAssetManager : public ::AAssetManager { + Guarded guarded_assetmanager; +}; - return newParcelFileDescriptor(env, fileDesc); +::AAssetManager* NdkAssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager) { + jlong assetmanager_handle = env->GetLongField(jassetmanager, gAssetManagerOffsets.mObject); + ::AAssetManager* am = reinterpret_cast<::AAssetManager*>(assetmanager_handle); + if (am == nullptr) { + jniThrowException(env, "java/lang/IllegalStateException", "AssetManager has been finalized!"); + return nullptr; + } + return am; } -static jobject android_content_AssetManager_openAssetFd(JNIEnv* env, jobject clazz, - jstring fileName, jlongArray outOffsets) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - ALOGV("openAssetFd in %p (Java object %p)\n", am, clazz); - - ScopedUtfChars fileName8(env, fileName); - if (fileName8.c_str() == NULL) { - return NULL; - } - - Asset* a = am->open(fileName8.c_str(), Asset::ACCESS_RANDOM); - - if (a == NULL) { - jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); - return NULL; - } - - //printf("Created Asset Stream: %p\n", a); - - return returnParcelFileDescriptor(env, a, outOffsets); +Guarded* AssetManagerForNdkAssetManager(::AAssetManager* assetmanager) { + if (assetmanager == nullptr) { + return nullptr; + } + return &reinterpret_cast(assetmanager)->guarded_assetmanager; } -static jlong android_content_AssetManager_openNonAssetNative(JNIEnv* env, jobject clazz, - jint cookie, - jstring fileName, - jint mode) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - - ALOGV("openNonAssetNative in %p (Java object %p)\n", am, clazz); - - ScopedUtfChars fileName8(env, fileName); - if (fileName8.c_str() == NULL) { - return -1; - } - - if (mode != Asset::ACCESS_UNKNOWN && mode != Asset::ACCESS_RANDOM - && mode != Asset::ACCESS_STREAMING && mode != Asset::ACCESS_BUFFER) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode"); - return -1; - } - - Asset* a = cookie - ? am->openNonAsset(static_cast(cookie), fileName8.c_str(), - (Asset::AccessMode)mode) - : am->openNonAsset(fileName8.c_str(), (Asset::AccessMode)mode); - - if (a == NULL) { - jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); - return -1; - } - - //printf("Created Asset Stream: %p\n", a); - - return reinterpret_cast(a); +Guarded* AssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager) { + return AssetManagerForNdkAssetManager(NdkAssetManagerForJavaObject(env, jassetmanager)); } -static jobject android_content_AssetManager_openNonAssetFdNative(JNIEnv* env, jobject clazz, - jint cookie, - jstring fileName, - jlongArray outOffsets) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - ALOGV("openNonAssetFd in %p (Java object %p)\n", am, clazz); - - ScopedUtfChars fileName8(env, fileName); - if (fileName8.c_str() == NULL) { - return NULL; - } - - Asset* a = cookie - ? am->openNonAsset(static_cast(cookie), fileName8.c_str(), Asset::ACCESS_RANDOM) - : am->openNonAsset(fileName8.c_str(), Asset::ACCESS_RANDOM); - - if (a == NULL) { - jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); - return NULL; - } - - //printf("Created Asset Stream: %p\n", a); - - return returnParcelFileDescriptor(env, a, outOffsets); +static Guarded& AssetManagerFromLong(jlong ptr) { + return *AssetManagerForNdkAssetManager(reinterpret_cast(ptr)); } -static jobjectArray android_content_AssetManager_list(JNIEnv* env, jobject clazz, - jstring fileName) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - ScopedUtfChars fileName8(env, fileName); - if (fileName8.c_str() == NULL) { - return NULL; - } - - AssetDir* dir = am->openDir(fileName8.c_str()); - - if (dir == NULL) { - jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); - return NULL; - } - - size_t N = dir->getFileCount(); - - jobjectArray array = env->NewObjectArray(dir->getFileCount(), - g_stringClass, NULL); - if (array == NULL) { - delete dir; - return NULL; - } - - for (size_t i=0; igetFileName(i); - jstring str = env->NewStringUTF(name.string()); - if (str == NULL) { - delete dir; - return NULL; - } - env->SetObjectArrayElement(array, i, str); - env->DeleteLocalRef(str); - } - - delete dir; - - return array; +static jobject ReturnParcelFileDescriptor(JNIEnv* env, std::unique_ptr asset, + jlongArray out_offsets) { + off64_t start_offset, length; + int fd = asset->openFileDescriptor(&start_offset, &length); + asset.reset(); + + if (fd < 0) { + jniThrowException(env, "java/io/FileNotFoundException", + "This file can not be opened as a file descriptor; it is probably " + "compressed"); + return nullptr; + } + + jlong* offsets = reinterpret_cast(env->GetPrimitiveArrayCritical(out_offsets, 0)); + if (offsets == nullptr) { + close(fd); + return nullptr; + } + + offsets[0] = start_offset; + offsets[1] = length; + + env->ReleasePrimitiveArrayCritical(out_offsets, offsets, 0); + + jobject file_desc = jniCreateFileDescriptor(env, fd); + if (file_desc == nullptr) { + close(fd); + return nullptr; + } + return newParcelFileDescriptor(env, file_desc); } -static void android_content_AssetManager_destroyAsset(JNIEnv* env, jobject clazz, - jlong assetHandle) -{ - Asset* a = reinterpret_cast(assetHandle); - - //printf("Destroying Asset Stream: %p\n", a); - - if (a == NULL) { - jniThrowNullPointerException(env, "asset"); - return; - } - - delete a; +static jint NativeGetGlobalAssetCount(JNIEnv* /*env*/, jobject /*clazz*/) { + return Asset::getGlobalCount(); } -static jint android_content_AssetManager_readAssetChar(JNIEnv* env, jobject clazz, - jlong assetHandle) -{ - Asset* a = reinterpret_cast(assetHandle); - - if (a == NULL) { - jniThrowNullPointerException(env, "asset"); - return -1; - } - - uint8_t b; - ssize_t res = a->read(&b, 1); - return res == 1 ? b : -1; +static jobject NativeGetAssetAllocations(JNIEnv* env, jobject /*clazz*/) { + String8 alloc = Asset::getAssetAllocations(); + if (alloc.length() <= 0) { + return nullptr; + } + return env->NewStringUTF(alloc.string()); } -static jint android_content_AssetManager_readAsset(JNIEnv* env, jobject clazz, - jlong assetHandle, jbyteArray bArray, - jint off, jint len) -{ - Asset* a = reinterpret_cast(assetHandle); - - if (a == NULL || bArray == NULL) { - jniThrowNullPointerException(env, "asset"); - return -1; - } - - if (len == 0) { - return 0; - } - - jsize bLen = env->GetArrayLength(bArray); - if (off < 0 || off >= bLen || len < 0 || len > bLen || (off+len) > bLen) { - jniThrowException(env, "java/lang/IndexOutOfBoundsException", ""); - return -1; - } - - jbyte* b = env->GetByteArrayElements(bArray, NULL); - ssize_t res = a->read(b+off, len); - env->ReleaseByteArrayElements(bArray, b, 0); - - if (res > 0) return static_cast(res); - - if (res < 0) { - jniThrowException(env, "java/io/IOException", ""); - } - return -1; +static jint NativeGetGlobalAssetManagerCount(JNIEnv* /*env*/, jobject /*clazz*/) { + // TODO(adamlesinski): Switch to AssetManager2. + return AssetManager::getGlobalCount(); } -static jlong android_content_AssetManager_seekAsset(JNIEnv* env, jobject clazz, - jlong assetHandle, - jlong offset, jint whence) -{ - Asset* a = reinterpret_cast(assetHandle); - - if (a == NULL) { - jniThrowNullPointerException(env, "asset"); - return -1; - } - - return a->seek( - offset, (whence > 0) ? SEEK_END : (whence < 0 ? SEEK_SET : SEEK_CUR)); +static jlong NativeCreate(JNIEnv* /*env*/, jclass /*clazz*/) { + // AssetManager2 needs to be protected by a lock. To avoid cache misses, we allocate the lock and + // AssetManager2 in a contiguous block (GuardedAssetManager). + return reinterpret_cast(new GuardedAssetManager()); } -static jlong android_content_AssetManager_getAssetLength(JNIEnv* env, jobject clazz, - jlong assetHandle) -{ - Asset* a = reinterpret_cast(assetHandle); - - if (a == NULL) { - jniThrowNullPointerException(env, "asset"); - return -1; - } - - return a->getLength(); +static void NativeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { + delete reinterpret_cast(ptr); } -static jlong android_content_AssetManager_getAssetRemainingLength(JNIEnv* env, jobject clazz, - jlong assetHandle) -{ - Asset* a = reinterpret_cast(assetHandle); - - if (a == NULL) { - jniThrowNullPointerException(env, "asset"); - return -1; +static void NativeSetApkAssets(JNIEnv* env, jclass /*clazz*/, jlong ptr, + jobjectArray apk_assets_array, jboolean invalidate_caches) { + const jsize apk_assets_len = env->GetArrayLength(apk_assets_array); + std::vector apk_assets; + apk_assets.reserve(apk_assets_len); + for (jsize i = 0; i < apk_assets_len; i++) { + jobject obj = env->GetObjectArrayElement(apk_assets_array, i); + if (obj == nullptr) { + std::string msg = StringPrintf("ApkAssets at index %d is null", i); + jniThrowNullPointerException(env, msg.c_str()); + return; } - return a->getRemainingLength(); -} - -static jint android_content_AssetManager_addAssetPath(JNIEnv* env, jobject clazz, - jstring path, jboolean appAsLib) -{ - ScopedUtfChars path8(env, path); - if (path8.c_str() == NULL) { - return 0; - } - - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; + jlong apk_assets_native_ptr = env->GetLongField(obj, gApkAssetsFields.native_ptr); + if (env->ExceptionCheck()) { + return; } + apk_assets.push_back(reinterpret_cast(apk_assets_native_ptr)); + } - int32_t cookie; - bool res = am->addAssetPath(String8(path8.c_str()), &cookie, appAsLib); - - return (res) ? static_cast(cookie) : 0; + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + assetmanager->SetApkAssets(apk_assets, invalidate_caches); } -static jint android_content_AssetManager_addOverlayPath(JNIEnv* env, jobject clazz, - jstring idmapPath) -{ - ScopedUtfChars idmapPath8(env, idmapPath); - if (idmapPath8.c_str() == NULL) { - return 0; - } - - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - - int32_t cookie; - bool res = am->addOverlayPath(String8(idmapPath8.c_str()), &cookie); - - return (res) ? (jint)cookie : 0; +static void NativeSetConfiguration(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint mcc, jint mnc, + jstring locale, jint orientation, jint touchscreen, jint density, + jint keyboard, jint keyboard_hidden, jint navigation, + jint screen_width, jint screen_height, + jint smallest_screen_width_dp, jint screen_width_dp, + jint screen_height_dp, jint screen_layout, jint ui_mode, + jint color_mode, jint major_version) { + ResTable_config configuration; + memset(&configuration, 0, sizeof(configuration)); + configuration.mcc = static_cast(mcc); + configuration.mnc = static_cast(mnc); + configuration.orientation = static_cast(orientation); + configuration.touchscreen = static_cast(touchscreen); + configuration.density = static_cast(density); + configuration.keyboard = static_cast(keyboard); + configuration.inputFlags = static_cast(keyboard_hidden); + configuration.navigation = static_cast(navigation); + configuration.screenWidth = static_cast(screen_width); + configuration.screenHeight = static_cast(screen_height); + configuration.smallestScreenWidthDp = static_cast(smallest_screen_width_dp); + configuration.screenWidthDp = static_cast(screen_width_dp); + configuration.screenHeightDp = static_cast(screen_height_dp); + configuration.screenLayout = static_cast(screen_layout); + configuration.uiMode = static_cast(ui_mode); + configuration.colorMode = static_cast(color_mode); + configuration.sdkVersion = static_cast(major_version); + + if (locale != nullptr) { + ScopedUtfChars locale_utf8(env, locale); + CHECK(locale_utf8.c_str() != nullptr); + configuration.setBcp47Locale(locale_utf8.c_str()); + } + + // Constants duplicated from Java class android.content.res.Configuration. + static const jint kScreenLayoutRoundMask = 0x300; + static const jint kScreenLayoutRoundShift = 8; + + // In Java, we use a 32bit integer for screenLayout, while we only use an 8bit integer + // in C++. We must extract the round qualifier out of the Java screenLayout and put it + // into screenLayout2. + configuration.screenLayout2 = + static_cast((screen_layout & kScreenLayoutRoundMask) >> kScreenLayoutRoundShift); + + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + assetmanager->SetConfiguration(configuration); } -static jint android_content_AssetManager_addAssetFd(JNIEnv* env, jobject clazz, - jobject fileDescriptor, jstring debugPathName, - jboolean appAsLib) -{ - ScopedUtfChars debugPathName8(env, debugPathName); +static jobject NativeGetAssignedPackageIdentifiers(JNIEnv* env, jclass /*clazz*/, jlong ptr) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); - int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); - if (fd < 0) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Bad FileDescriptor"); - return 0; - } + jobject sparse_array = + env->NewObject(gSparseArrayOffsets.classObject, gSparseArrayOffsets.constructor); - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } + if (sparse_array == nullptr) { + // An exception is pending. + return nullptr; + } - int dupfd = ::dup(fd); - if (dupfd < 0) { - jniThrowIOException(env, errno); - return 0; + assetmanager->ForEachPackage([&](const std::string& package_name, uint8_t package_id) { + jstring jpackage_name = env->NewStringUTF(package_name.c_str()); + if (jpackage_name == nullptr) { + // An exception is pending. + return; } - int32_t cookie; - bool res = am->addAssetFd(dupfd, String8(debugPathName8.c_str()), &cookie, appAsLib); - - return (res) ? static_cast(cookie) : 0; -} - -static jboolean android_content_AssetManager_isUpToDate(JNIEnv* env, jobject clazz) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return JNI_TRUE; - } - return am->isUpToDate() ? JNI_TRUE : JNI_FALSE; + env->CallVoidMethod(sparse_array, gSparseArrayOffsets.put, static_cast(package_id), + jpackage_name); + }); + return sparse_array; } -static jobjectArray getLocales(JNIEnv* env, jobject clazz, bool includeSystemLocales) -{ - Vector locales; - - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; +static jobjectArray NativeList(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring path) { + ScopedUtfChars path_utf8(env, path); + if (path_utf8.c_str() == nullptr) { + // This will throw NPE. + return nullptr; + } + + std::vector all_file_paths; + { + StringPiece normalized_path = path_utf8.c_str(); + if (normalized_path.data()[0] == '/') { + normalized_path = normalized_path.substr(1); + } + std::string root_path = StringPrintf("assets/%s", normalized_path.data()); + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + for (const ApkAssets* assets : assetmanager->GetApkAssets()) { + assets->ForEachFile(root_path, [&](const StringPiece& file_path, FileType type) { + if (type == FileType::kFileTypeRegular) { + all_file_paths.push_back(file_path.to_string()); + } + }); } + } - am->getLocales(&locales, includeSystemLocales); + jobjectArray array = env->NewObjectArray(all_file_paths.size(), g_stringClass, nullptr); + if (array == nullptr) { + return nullptr; + } - const int N = locales.size(); + jsize index = 0; + for (const std::string& file_path : all_file_paths) { + jstring java_string = env->NewStringUTF(file_path.c_str()); - jobjectArray result = env->NewObjectArray(N, g_stringClass, NULL); - if (result == NULL) { - return NULL; + // Check for errors creating the strings (if malformed or no memory). + if (env->ExceptionCheck()) { + return nullptr; } - for (int i=0; iNewStringUTF(locales[i].string()); - if (str == NULL) { - return NULL; - } - env->SetObjectArrayElement(result, i, str); - env->DeleteLocalRef(str); - } + env->SetObjectArrayElement(array, index++, java_string); - return result; + // If we have a large amount of string in our array, we might overflow the + // local reference table of the VM. + env->DeleteLocalRef(java_string); + } + return array; } -static jobjectArray android_content_AssetManager_getLocales(JNIEnv* env, jobject clazz) -{ - return getLocales(env, clazz, true /* include system locales */); +static jlong NativeOpenAsset(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring asset_path, + jint access_mode) { + ScopedUtfChars asset_path_utf8(env, asset_path); + if (asset_path_utf8.c_str() == nullptr) { + // This will throw NPE. + return 0; + } + + if (access_mode != Asset::ACCESS_UNKNOWN && access_mode != Asset::ACCESS_RANDOM && + access_mode != Asset::ACCESS_STREAMING && access_mode != Asset::ACCESS_BUFFER) { + jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode"); + return 0; + } + + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + std::unique_ptr asset = + assetmanager->Open(asset_path_utf8.c_str(), static_cast(access_mode)); + if (!asset) { + jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); + return 0; + } + return reinterpret_cast(asset.release()); } -static jobjectArray android_content_AssetManager_getNonSystemLocales(JNIEnv* env, jobject clazz) -{ - return getLocales(env, clazz, false /* don't include system locales */); +static jobject NativeOpenAssetFd(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring asset_path, + jlongArray out_offsets) { + ScopedUtfChars asset_path_utf8(env, asset_path); + if (asset_path_utf8.c_str() == nullptr) { + // This will throw NPE. + return nullptr; + } + + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + std::unique_ptr asset = assetmanager->Open(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM); + if (!asset) { + jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); + return nullptr; + } + return ReturnParcelFileDescriptor(env, std::move(asset), out_offsets); } -static jobject constructConfigurationObject(JNIEnv* env, const ResTable_config& config) { - jobject result = env->NewObject(gConfigurationOffsets.classObject, - gConfigurationOffsets.constructor); - if (result == NULL) { - return NULL; - } - - env->SetIntField(result, gConfigurationOffsets.mSmallestScreenWidthDpOffset, - config.smallestScreenWidthDp); - env->SetIntField(result, gConfigurationOffsets.mScreenWidthDpOffset, config.screenWidthDp); - env->SetIntField(result, gConfigurationOffsets.mScreenHeightDpOffset, config.screenHeightDp); - - return result; +static jlong NativeOpenNonAsset(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint jcookie, + jstring asset_path, jint access_mode) { + ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie); + ScopedUtfChars asset_path_utf8(env, asset_path); + if (asset_path_utf8.c_str() == nullptr) { + // This will throw NPE. + return 0; + } + + if (access_mode != Asset::ACCESS_UNKNOWN && access_mode != Asset::ACCESS_RANDOM && + access_mode != Asset::ACCESS_STREAMING && access_mode != Asset::ACCESS_BUFFER) { + jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode"); + return 0; + } + + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + std::unique_ptr asset; + if (cookie != kInvalidCookie) { + asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie, + static_cast(access_mode)); + } else { + asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), + static_cast(access_mode)); + } + + if (!asset) { + jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); + return 0; + } + return reinterpret_cast(asset.release()); } -static jobjectArray getSizeConfigurationsInternal(JNIEnv* env, - const Vector& configs) { - const int N = configs.size(); - jobjectArray result = env->NewObjectArray(N, gConfigurationOffsets.classObject, NULL); - if (result == NULL) { - return NULL; - } - - for (int i=0; iDeleteLocalRef(result); - return NULL; - } - - env->SetObjectArrayElement(result, i, config); - env->DeleteLocalRef(config); - } - - return result; +static jobject NativeOpenNonAssetFd(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint jcookie, + jstring asset_path, jlongArray out_offsets) { + ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie); + ScopedUtfChars asset_path_utf8(env, asset_path); + if (asset_path_utf8.c_str() == nullptr) { + // This will throw NPE. + return nullptr; + } + + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + std::unique_ptr asset; + if (cookie != kInvalidCookie) { + asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie, Asset::ACCESS_RANDOM); + } else { + asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM); + } + + if (!asset) { + jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); + return nullptr; + } + return ReturnParcelFileDescriptor(env, std::move(asset), out_offsets); } -static jobjectArray android_content_AssetManager_getSizeConfigurations(JNIEnv* env, jobject clazz) { - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - const ResTable& res(am->getResources()); - Vector configs; - res.getConfigurations(&configs, false /* ignoreMipmap */, true /* ignoreAndroidPackage */); - - return getSizeConfigurationsInternal(env, configs); +static jlong NativeOpenXmlAsset(JNIEnv* env, jobject /*clazz*/, jlong ptr, jint jcookie, + jstring asset_path) { + ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie); + ScopedUtfChars asset_path_utf8(env, asset_path); + if (asset_path_utf8.c_str() == nullptr) { + // This will throw NPE. + return 0; + } + + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + std::unique_ptr asset; + if (cookie != kInvalidCookie) { + asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie, Asset::ACCESS_RANDOM); + } else { + asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM, &cookie); + } + + if (!asset) { + jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); + return 0; + } + + // May be nullptr. + const DynamicRefTable* dynamic_ref_table = assetmanager->GetDynamicRefTableForCookie(cookie); + + std::unique_ptr xml_tree = util::make_unique(dynamic_ref_table); + status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true); + asset.reset(); + + if (err != NO_ERROR) { + jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file"); + return 0; + } + return reinterpret_cast(xml_tree.release()); } -static void android_content_AssetManager_setConfiguration(JNIEnv* env, jobject clazz, - jint mcc, jint mnc, - jstring locale, jint orientation, - jint touchscreen, jint density, - jint keyboard, jint keyboardHidden, - jint navigation, - jint screenWidth, jint screenHeight, - jint smallestScreenWidthDp, - jint screenWidthDp, jint screenHeightDp, - jint screenLayout, jint uiMode, - jint colorMode, jint sdkVersion) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return; - } - - ResTable_config config; - memset(&config, 0, sizeof(config)); - - const char* locale8 = locale != NULL ? env->GetStringUTFChars(locale, NULL) : NULL; - - // Constants duplicated from Java class android.content.res.Configuration. - static const jint kScreenLayoutRoundMask = 0x300; - static const jint kScreenLayoutRoundShift = 8; - - config.mcc = (uint16_t)mcc; - config.mnc = (uint16_t)mnc; - config.orientation = (uint8_t)orientation; - config.touchscreen = (uint8_t)touchscreen; - config.density = (uint16_t)density; - config.keyboard = (uint8_t)keyboard; - config.inputFlags = (uint8_t)keyboardHidden; - config.navigation = (uint8_t)navigation; - config.screenWidth = (uint16_t)screenWidth; - config.screenHeight = (uint16_t)screenHeight; - config.smallestScreenWidthDp = (uint16_t)smallestScreenWidthDp; - config.screenWidthDp = (uint16_t)screenWidthDp; - config.screenHeightDp = (uint16_t)screenHeightDp; - config.screenLayout = (uint8_t)screenLayout; - config.uiMode = (uint8_t)uiMode; - config.colorMode = (uint8_t)colorMode; - config.sdkVersion = (uint16_t)sdkVersion; - config.minorVersion = 0; - - // In Java, we use a 32bit integer for screenLayout, while we only use an 8bit integer - // in C++. We must extract the round qualifier out of the Java screenLayout and put it - // into screenLayout2. - config.screenLayout2 = - (uint8_t)((screenLayout & kScreenLayoutRoundMask) >> kScreenLayoutRoundShift); - - am->setConfiguration(config, locale8); - - if (locale != NULL) env->ReleaseStringUTFChars(locale, locale8); +static jint NativeGetResourceValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid, + jshort density, jobject typed_value, + jboolean resolve_references) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + Res_value value; + ResTable_config selected_config; + uint32_t flags; + ApkAssetsCookie cookie = + assetmanager->GetResource(static_cast(resid), false /*may_be_bag*/, + static_cast(density), &value, &selected_config, &flags); + if (cookie == kInvalidCookie) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + + uint32_t ref = static_cast(resid); + if (resolve_references) { + cookie = assetmanager->ResolveReference(cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + } + return CopyValue(env, cookie, value, ref, flags, &selected_config, typed_value); } -static jint android_content_AssetManager_getResourceIdentifier(JNIEnv* env, jobject clazz, - jstring name, - jstring defType, - jstring defPackage) -{ - ScopedStringChars name16(env, name); - if (name16.get() == NULL) { - return 0; - } - - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - - const char16_t* defType16 = reinterpret_cast(defType) - ? reinterpret_cast(env->GetStringChars(defType, NULL)) - : NULL; - jsize defTypeLen = defType - ? env->GetStringLength(defType) : 0; - const char16_t* defPackage16 = reinterpret_cast(defPackage) - ? reinterpret_cast(env->GetStringChars(defPackage, - NULL)) - : NULL; - jsize defPackageLen = defPackage - ? env->GetStringLength(defPackage) : 0; - - jint ident = am->getResources().identifierForName( - reinterpret_cast(name16.get()), name16.size(), - defType16, defTypeLen, defPackage16, defPackageLen); - - if (defPackage16) { - env->ReleaseStringChars(defPackage, - reinterpret_cast(defPackage16)); - } - if (defType16) { - env->ReleaseStringChars(defType, - reinterpret_cast(defType16)); - } - - return ident; +static jint NativeGetResourceBagValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid, + jint bag_entry_id, jobject typed_value) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); + if (bag == nullptr) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + + uint32_t type_spec_flags = bag->type_spec_flags; + ApkAssetsCookie cookie = kInvalidCookie; + const Res_value* bag_value = nullptr; + for (const ResolvedBag::Entry& entry : bag) { + if (entry.key == static_cast(bag_entry_id)) { + cookie = entry.cookie; + bag_value = &entry.value; + + // Keep searching (the old implementation did that). + } + } + + if (cookie == kInvalidCookie) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + + Res_value value = *bag_value; + uint32_t ref = static_cast(resid); + ResTable_config selected_config; + cookie = assetmanager->ResolveReference(cookie, &value, &selected_config, &type_spec_flags, &ref); + if (cookie == kInvalidCookie) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + return CopyValue(env, cookie, value, ref, type_spec_flags, nullptr, typed_value); } -static jstring android_content_AssetManager_getResourceName(JNIEnv* env, jobject clazz, - jint resid) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - ResTable::resource_name name; - if (!am->getResources().getResourceName(resid, true, &name)) { - return NULL; - } - - String16 str; - if (name.package != NULL) { - str.setTo(name.package, name.packageLen); - } - if (name.type8 != NULL || name.type != NULL) { - if (str.size() > 0) { - char16_t div = ':'; - str.append(&div, 1); - } - if (name.type8 != NULL) { - str.append(String16(name.type8, name.typeLen)); - } else { - str.append(name.type, name.typeLen); - } - } - if (name.name8 != NULL || name.name != NULL) { - if (str.size() > 0) { - char16_t div = '/'; - str.append(&div, 1); - } - if (name.name8 != NULL) { - str.append(String16(name.name8, name.nameLen)); - } else { - str.append(name.name, name.nameLen); - } - } - - return env->NewString((const jchar*)str.string(), str.size()); +static jintArray NativeGetStyleAttributes(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); + if (bag == nullptr) { + return nullptr; + } + + jintArray array = env->NewIntArray(bag->entry_count); + if (env->ExceptionCheck()) { + return nullptr; + } + + for (uint32_t i = 0; i < bag->entry_count; i++) { + jint attr_resid = bag->entries[i].key; + env->SetIntArrayRegion(array, i, 1, &attr_resid); + } + return array; } -static jstring android_content_AssetManager_getResourcePackageName(JNIEnv* env, jobject clazz, - jint resid) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - ResTable::resource_name name; - if (!am->getResources().getResourceName(resid, true, &name)) { - return NULL; - } - - if (name.package != NULL) { - return env->NewString((const jchar*)name.package, name.packageLen); - } - - return NULL; +static jobjectArray NativeGetResourceStringArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, + jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); + if (bag == nullptr) { + return nullptr; + } + + jobjectArray array = env->NewObjectArray(bag->entry_count, g_stringClass, nullptr); + if (array == nullptr) { + return nullptr; + } + + for (uint32_t i = 0; i < bag->entry_count; i++) { + const ResolvedBag::Entry& entry = bag->entries[i]; + + // Resolve any references to their final value. + Res_value value = entry.value; + ResTable_config selected_config; + uint32_t flags; + uint32_t ref; + ApkAssetsCookie cookie = + assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { + return nullptr; + } + + if (value.dataType == Res_value::TYPE_STRING) { + const ApkAssets* apk_assets = assetmanager->GetApkAssets()[cookie]; + const ResStringPool* pool = apk_assets->GetLoadedArsc()->GetStringPool(); + + jstring java_string = nullptr; + size_t str_len; + const char* str_utf8 = pool->string8At(value.data, &str_len); + if (str_utf8 != nullptr) { + java_string = env->NewStringUTF(str_utf8); + } else { + const char16_t* str_utf16 = pool->stringAt(value.data, &str_len); + java_string = env->NewString(reinterpret_cast(str_utf16), str_len); + } + + // Check for errors creating the strings (if malformed or no memory). + if (env->ExceptionCheck()) { + return nullptr; + } + + env->SetObjectArrayElement(array, i, java_string); + + // If we have a large amount of string in our array, we might overflow the + // local reference table of the VM. + env->DeleteLocalRef(java_string); + } + } + return array; } -static jstring android_content_AssetManager_getResourceTypeName(JNIEnv* env, jobject clazz, - jint resid) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - ResTable::resource_name name; - if (!am->getResources().getResourceName(resid, true, &name)) { - return NULL; - } - - if (name.type8 != NULL) { - return env->NewStringUTF(name.type8); - } - - if (name.type != NULL) { - return env->NewString((const jchar*)name.type, name.typeLen); - } +static jintArray NativeGetResourceStringArrayInfo(JNIEnv* env, jclass /*clazz*/, jlong ptr, + jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); + if (bag == nullptr) { + return nullptr; + } + + jintArray array = env->NewIntArray(bag->entry_count * 2); + if (array == nullptr) { + return nullptr; + } + + jint* buffer = reinterpret_cast(env->GetPrimitiveArrayCritical(array, nullptr)); + if (buffer == nullptr) { + return nullptr; + } + + for (size_t i = 0; i < bag->entry_count; i++) { + const ResolvedBag::Entry& entry = bag->entries[i]; + Res_value value = entry.value; + ResTable_config selected_config; + uint32_t flags; + uint32_t ref; + ApkAssetsCookie cookie = + assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { + env->ReleasePrimitiveArrayCritical(array, buffer, JNI_ABORT); + return nullptr; + } + + jint string_index = -1; + if (value.dataType == Res_value::TYPE_STRING) { + string_index = static_cast(value.data); + } + + buffer[i * 2] = ApkAssetsCookieToJavaCookie(cookie); + buffer[(i * 2) + 1] = string_index; + } + env->ReleasePrimitiveArrayCritical(array, buffer, 0); + return array; +} - return NULL; +static jintArray NativeGetResourceIntArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); + if (bag == nullptr) { + return nullptr; + } + + jintArray array = env->NewIntArray(bag->entry_count); + if (array == nullptr) { + return nullptr; + } + + jint* buffer = reinterpret_cast(env->GetPrimitiveArrayCritical(array, nullptr)); + if (buffer == nullptr) { + return nullptr; + } + + for (size_t i = 0; i < bag->entry_count; i++) { + const ResolvedBag::Entry& entry = bag->entries[i]; + Res_value value = entry.value; + ResTable_config selected_config; + uint32_t flags; + uint32_t ref; + ApkAssetsCookie cookie = + assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { + env->ReleasePrimitiveArrayCritical(array, buffer, JNI_ABORT); + return nullptr; + } + + if (value.dataType >= Res_value::TYPE_FIRST_INT && value.dataType <= Res_value::TYPE_LAST_INT) { + buffer[i] = static_cast(value.data); + } + } + env->ReleasePrimitiveArrayCritical(array, buffer, 0); + return array; } -static jstring android_content_AssetManager_getResourceEntryName(JNIEnv* env, jobject clazz, - jint resid) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } +static jint NativeGetResourceArraySize(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); + if (bag == nullptr) { + return -1; + } + return static_cast(bag->entry_count); +} - ResTable::resource_name name; - if (!am->getResources().getResourceName(resid, true, &name)) { - return NULL; - } +static jint NativeGetResourceArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid, + jintArray out_data) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); + if (bag == nullptr) { + return -1; + } - if (name.name8 != NULL) { - return env->NewStringUTF(name.name8); - } + const jsize out_data_length = env->GetArrayLength(out_data); + if (env->ExceptionCheck()) { + return -1; + } - if (name.name != NULL) { - return env->NewString((const jchar*)name.name, name.nameLen); - } + if (static_cast(bag->entry_count) > out_data_length * STYLE_NUM_ENTRIES) { + jniThrowException(env, "java/lang/IllegalArgumentException", "Input array is not large enough"); + return -1; + } - return NULL; + jint* buffer = reinterpret_cast(env->GetPrimitiveArrayCritical(out_data, nullptr)); + if (buffer == nullptr) { + return -1; + } + + jint* cursor = buffer; + for (size_t i = 0; i < bag->entry_count; i++) { + const ResolvedBag::Entry& entry = bag->entries[i]; + Res_value value = entry.value; + ResTable_config selected_config; + selected_config.density = 0; + uint32_t flags = bag->type_spec_flags; + uint32_t ref; + ApkAssetsCookie cookie = + assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { + env->ReleasePrimitiveArrayCritical(out_data, buffer, JNI_ABORT); + return -1; + } + + // Deal with the special @null value -- it turns back to TYPE_NULL. + if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { + value.dataType = Res_value::TYPE_NULL; + value.data = Res_value::DATA_NULL_UNDEFINED; + } + + cursor[STYLE_TYPE] = static_cast(value.dataType); + cursor[STYLE_DATA] = static_cast(value.data); + cursor[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); + cursor[STYLE_RESOURCE_ID] = static_cast(ref); + cursor[STYLE_CHANGING_CONFIGURATIONS] = static_cast(flags); + cursor[STYLE_DENSITY] = static_cast(selected_config.density); + cursor += STYLE_NUM_ENTRIES; + } + env->ReleasePrimitiveArrayCritical(out_data, buffer, 0); + return static_cast(bag->entry_count); } -static jint android_content_AssetManager_loadResourceValue(JNIEnv* env, jobject clazz, - jint ident, - jshort density, - jobject outValue, - jboolean resolve) -{ - if (outValue == NULL) { - jniThrowNullPointerException(env, "outValue"); - return 0; - } - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - const ResTable& res(am->getResources()); - - Res_value value; - ResTable_config config; - uint32_t typeSpecFlags; - ssize_t block = res.getResource(ident, &value, false, density, &typeSpecFlags, &config); - if (kThrowOnBadId) { - if (block == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return 0; - } - } - uint32_t ref = ident; - if (resolve) { - block = res.resolveReference(&value, block, &ref, &typeSpecFlags, &config); - if (kThrowOnBadId) { - if (block == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return 0; - } - } - } - if (block >= 0) { - return copyValue(env, outValue, &res, value, ref, block, typeSpecFlags, &config); - } - - return static_cast(block); +static jint NativeGetResourceIdentifier(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring name, + jstring def_type, jstring def_package) { + ScopedUtfChars name_utf8(env, name); + if (name_utf8.c_str() == nullptr) { + // This will throw NPE. + return 0; + } + + std::string type; + if (def_type != nullptr) { + ScopedUtfChars type_utf8(env, def_type); + CHECK(type_utf8.c_str() != nullptr); + type = type_utf8.c_str(); + } + + std::string package; + if (def_package != nullptr) { + ScopedUtfChars package_utf8(env, def_package); + CHECK(package_utf8.c_str() != nullptr); + package = package_utf8.c_str(); + } + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + return static_cast(assetmanager->GetResourceId(name_utf8.c_str(), type, package)); } -static jint android_content_AssetManager_loadResourceBagValue(JNIEnv* env, jobject clazz, - jint ident, jint bagEntryId, - jobject outValue, jboolean resolve) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - const ResTable& res(am->getResources()); - - // Now lock down the resource object and start pulling stuff from it. - res.lock(); - - ssize_t block = -1; - Res_value value; +static jstring NativeGetResourceName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + AssetManager2::ResourceName name; + if (!assetmanager->GetResourceName(static_cast(resid), &name)) { + return nullptr; + } - const ResTable::bag_entry* entry = NULL; - uint32_t typeSpecFlags; - ssize_t entryCount = res.getBagLocked(ident, &entry, &typeSpecFlags); + std::string result; + if (name.package != nullptr) { + result.append(name.package, name.package_len); + } - for (ssize_t i=0; imap.name.ident) { - block = entry->stringBlock; - value = entry->map.value; - } - entry++; + if (name.type != nullptr || name.type16 != nullptr) { + if (!result.empty()) { + result += ":"; } - res.unlock(); - - if (block < 0) { - return static_cast(block); + if (name.type != nullptr) { + result.append(name.type, name.type_len); + } else { + result += util::Utf16ToUtf8(StringPiece16(name.type16, name.type_len)); } + } - uint32_t ref = ident; - if (resolve) { - block = res.resolveReference(&value, block, &ref, &typeSpecFlags); - if (kThrowOnBadId) { - if (block == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return 0; - } - } - } - if (block >= 0) { - return copyValue(env, outValue, &res, value, ref, block, typeSpecFlags); + if (name.entry != nullptr || name.entry16 != nullptr) { + if (!result.empty()) { + result += "/"; } - return static_cast(block); -} - -static jint android_content_AssetManager_getStringBlockCount(JNIEnv* env, jobject clazz) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; + if (name.entry != nullptr) { + result.append(name.entry, name.entry_len); + } else { + result += util::Utf16ToUtf8(StringPiece16(name.entry16, name.entry_len)); } - return am->getResources().getTableCount(); + } + return env->NewStringUTF(result.c_str()); } -static jlong android_content_AssetManager_getNativeStringBlock(JNIEnv* env, jobject clazz, - jint block) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - return reinterpret_cast(am->getResources().getTableStringBlock(block)); +static jstring NativeGetResourcePackageName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + AssetManager2::ResourceName name; + if (!assetmanager->GetResourceName(static_cast(resid), &name)) { + return nullptr; + } + + if (name.package != nullptr) { + return env->NewStringUTF(name.package); + } + return nullptr; } -static jstring android_content_AssetManager_getCookieName(JNIEnv* env, jobject clazz, - jint cookie) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - String8 name(am->getAssetPath(static_cast(cookie))); - if (name.length() == 0) { - jniThrowException(env, "java/lang/IndexOutOfBoundsException", "Empty cookie name"); - return NULL; - } - jstring str = env->NewStringUTF(name.string()); - return str; +static jstring NativeGetResourceTypeName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + AssetManager2::ResourceName name; + if (!assetmanager->GetResourceName(static_cast(resid), &name)) { + return nullptr; + } + + if (name.type != nullptr) { + return env->NewStringUTF(name.type); + } else if (name.type16 != nullptr) { + return env->NewString(reinterpret_cast(name.type16), name.type_len); + } + return nullptr; } -static jobject android_content_AssetManager_getAssignedPackageIdentifiers(JNIEnv* env, jobject clazz) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - - const ResTable& res = am->getResources(); - - jobject sparseArray = env->NewObject(gSparseArrayOffsets.classObject, - gSparseArrayOffsets.constructor); - const size_t N = res.getBasePackageCount(); - for (size_t i = 0; i < N; i++) { - const String16 name = res.getBasePackageName(i); - env->CallVoidMethod( - sparseArray, gSparseArrayOffsets.put, - static_cast(res.getBasePackageId(i)), - env->NewString(reinterpret_cast(name.string()), - name.size())); - } - return sparseArray; +static jstring NativeGetResourceEntryName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + AssetManager2::ResourceName name; + if (!assetmanager->GetResourceName(static_cast(resid), &name)) { + return nullptr; + } + + if (name.entry != nullptr) { + return env->NewStringUTF(name.entry); + } else if (name.entry16 != nullptr) { + return env->NewString(reinterpret_cast(name.entry16), name.entry_len); + } + return nullptr; } -static jlong android_content_AssetManager_newTheme(JNIEnv* env, jobject clazz) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - return reinterpret_cast(new ResTable::Theme(am->getResources())); +static jobjectArray NativeGetLocales(JNIEnv* env, jclass /*class*/, jlong ptr, + jboolean exclude_system) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + std::set locales = + assetmanager->GetResourceLocales(exclude_system, true /*merge_equivalent_languages*/); + + jobjectArray array = env->NewObjectArray(locales.size(), g_stringClass, nullptr); + if (array == nullptr) { + return nullptr; + } + + size_t idx = 0; + for (const std::string& locale : locales) { + jstring java_string = env->NewStringUTF(locale.c_str()); + if (java_string == nullptr) { + return nullptr; + } + env->SetObjectArrayElement(array, idx++, java_string); + env->DeleteLocalRef(java_string); + } + return array; } -static void android_content_AssetManager_deleteTheme(JNIEnv* env, jobject clazz, - jlong themeHandle) -{ - ResTable::Theme* theme = reinterpret_cast(themeHandle); - delete theme; +static jobject ConstructConfigurationObject(JNIEnv* env, const ResTable_config& config) { + jobject result = + env->NewObject(gConfigurationOffsets.classObject, gConfigurationOffsets.constructor); + if (result == nullptr) { + return nullptr; + } + + env->SetIntField(result, gConfigurationOffsets.mSmallestScreenWidthDpOffset, + config.smallestScreenWidthDp); + env->SetIntField(result, gConfigurationOffsets.mScreenWidthDpOffset, config.screenWidthDp); + env->SetIntField(result, gConfigurationOffsets.mScreenHeightDpOffset, config.screenHeightDp); + return result; } -static void android_content_AssetManager_applyThemeStyle(JNIEnv* env, jobject clazz, - jlong themeHandle, - jint styleRes, - jboolean force) -{ - ResTable::Theme* theme = reinterpret_cast(themeHandle); - theme->applyStyle(styleRes, force ? true : false); -} - -static void android_content_AssetManager_copyTheme(JNIEnv* env, jobject clazz, - jlong destHandle, jlong srcHandle) -{ - ResTable::Theme* dest = reinterpret_cast(destHandle); - ResTable::Theme* src = reinterpret_cast(srcHandle); - dest->setTo(*src); -} +static jobjectArray NativeGetSizeConfigurations(JNIEnv* env, jclass /*clazz*/, jlong ptr) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + std::set configurations = + assetmanager->GetResourceConfigurations(true /*exclude_system*/, false /*exclude_mipmap*/); -static void android_content_AssetManager_clearTheme(JNIEnv* env, jobject clazz, jlong themeHandle) -{ - ResTable::Theme* theme = reinterpret_cast(themeHandle); - theme->clear(); -} + jobjectArray array = + env->NewObjectArray(configurations.size(), gConfigurationOffsets.classObject, nullptr); + if (array == nullptr) { + return nullptr; + } -static jint android_content_AssetManager_loadThemeAttributeValue( - JNIEnv* env, jobject clazz, jlong themeHandle, jint ident, jobject outValue, jboolean resolve) -{ - ResTable::Theme* theme = reinterpret_cast(themeHandle); - const ResTable& res(theme->getResTable()); - - Res_value value; - // XXX value could be different in different configs! - uint32_t typeSpecFlags = 0; - ssize_t block = theme->getAttribute(ident, &value, &typeSpecFlags); - uint32_t ref = 0; - if (resolve) { - block = res.resolveReference(&value, block, &ref, &typeSpecFlags); - if (kThrowOnBadId) { - if (block == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return 0; - } - } + size_t idx = 0; + for (const ResTable_config& configuration : configurations) { + jobject java_configuration = ConstructConfigurationObject(env, configuration); + if (java_configuration == nullptr) { + return nullptr; } - 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(themeHandle); - return theme->getChangingConfigurations(); + env->SetObjectArrayElement(array, idx++, java_configuration); + env->DeleteLocalRef(java_configuration); + } + return array; } -static void android_content_AssetManager_dumpTheme(JNIEnv* env, jobject clazz, - jlong themeHandle, jint pri, - jstring tag, jstring prefix) -{ - ResTable::Theme* theme = reinterpret_cast(themeHandle); - const ResTable& res(theme->getResTable()); - (void)res; - - // XXX Need to use params. - theme->dumpToLog(); +static void NativeApplyStyle(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr, + jint def_style_attr, jint def_style_resid, jlong xml_parser_ptr, + jintArray java_attrs, jlong out_values_ptr, jlong out_indices_ptr) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + Theme* theme = reinterpret_cast(theme_ptr); + CHECK(theme->GetAssetManager() == &(*assetmanager)); + (void) assetmanager; + + ResXMLParser* xml_parser = reinterpret_cast(xml_parser_ptr); + uint32_t* out_values = reinterpret_cast(out_values_ptr); + uint32_t* out_indices = reinterpret_cast(out_indices_ptr); + + jsize attrs_len = env->GetArrayLength(java_attrs); + jint* attrs = reinterpret_cast(env->GetPrimitiveArrayCritical(java_attrs, nullptr)); + if (attrs == nullptr) { + return; + } + + ApplyStyle(theme, xml_parser, static_cast(def_style_attr), + static_cast(def_style_resid), reinterpret_cast(attrs), attrs_len, + out_values, out_indices); + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); } -static jboolean android_content_AssetManager_resolveAttrs(JNIEnv* env, jobject clazz, - jlong themeToken, - jint defStyleAttr, - jint defStyleRes, - jintArray inValues, - jintArray attrs, - jintArray outValues, - jintArray outIndices) -{ - if (themeToken == 0) { - jniThrowNullPointerException(env, "theme token"); - return JNI_FALSE; - } - if (attrs == NULL) { - jniThrowNullPointerException(env, "attrs"); - return JNI_FALSE; - } - if (outValues == NULL) { - jniThrowNullPointerException(env, "out values"); - return JNI_FALSE; - } - - const jsize NI = env->GetArrayLength(attrs); - const jsize NV = env->GetArrayLength(outValues); - if (NV < (NI*STYLE_NUM_ENTRIES)) { - jniThrowException(env, "java/lang/IndexOutOfBoundsException", "out values too small"); +static jboolean NativeResolveAttrs(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr, + jint def_style_attr, jint def_style_resid, jintArray java_values, + jintArray java_attrs, jintArray out_java_values, + jintArray out_java_indices) { + const jsize attrs_len = env->GetArrayLength(java_attrs); + const jsize out_values_len = env->GetArrayLength(out_java_values); + if (out_values_len < (attrs_len * STYLE_NUM_ENTRIES)) { + jniThrowException(env, "java/lang/IndexOutOfBoundsException", "outValues too small"); + return JNI_FALSE; + } + + jint* attrs = reinterpret_cast(env->GetPrimitiveArrayCritical(java_attrs, nullptr)); + if (attrs == nullptr) { + return JNI_FALSE; + } + + jint* values = nullptr; + jsize values_len = 0; + if (java_values != nullptr) { + values_len = env->GetArrayLength(java_values); + values = reinterpret_cast(env->GetPrimitiveArrayCritical(java_values, nullptr)); + if (values == nullptr) { + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + return JNI_FALSE; + } + } + + jint* out_values = + reinterpret_cast(env->GetPrimitiveArrayCritical(out_java_values, nullptr)); + if (out_values == nullptr) { + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + if (values != nullptr) { + env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT); + } + return JNI_FALSE; + } + + jint* out_indices = nullptr; + if (out_java_indices != nullptr) { + jsize out_indices_len = env->GetArrayLength(out_java_indices); + if (out_indices_len > attrs_len) { + out_indices = + reinterpret_cast(env->GetPrimitiveArrayCritical(out_java_indices, nullptr)); + if (out_indices == nullptr) { + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + if (values != nullptr) { + env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT); + } + env->ReleasePrimitiveArrayCritical(out_java_values, out_values, JNI_ABORT); return JNI_FALSE; - } + } + } + } + + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + Theme* theme = reinterpret_cast(theme_ptr); + CHECK(theme->GetAssetManager() == &(*assetmanager)); + (void) assetmanager; + + bool result = ResolveAttrs( + theme, static_cast(def_style_attr), static_cast(def_style_resid), + reinterpret_cast(values), values_len, reinterpret_cast(attrs), + attrs_len, reinterpret_cast(out_values), reinterpret_cast(out_indices)); + if (out_indices != nullptr) { + env->ReleasePrimitiveArrayCritical(out_java_indices, out_indices, 0); + } + + env->ReleasePrimitiveArrayCritical(out_java_values, out_values, 0); + if (values != nullptr) { + env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT); + } + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + return result ? JNI_TRUE : JNI_FALSE; +} - jint* src = (jint*)env->GetPrimitiveArrayCritical(attrs, 0); - if (src == NULL) { +static jboolean NativeRetrieveAttributes(JNIEnv* env, jclass /*clazz*/, jlong ptr, + jlong xml_parser_ptr, jintArray java_attrs, + jintArray out_java_values, jintArray out_java_indices) { + const jsize attrs_len = env->GetArrayLength(java_attrs); + const jsize out_values_len = env->GetArrayLength(out_java_values); + if (out_values_len < (attrs_len * STYLE_NUM_ENTRIES)) { + jniThrowException(env, "java/lang/IndexOutOfBoundsException", "outValues too small"); + return JNI_FALSE; + } + + jint* attrs = reinterpret_cast(env->GetPrimitiveArrayCritical(java_attrs, nullptr)); + if (attrs == nullptr) { + return JNI_FALSE; + } + + jint* out_values = + reinterpret_cast(env->GetPrimitiveArrayCritical(out_java_values, nullptr)); + if (out_values == nullptr) { + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + return JNI_FALSE; + } + + jint* out_indices = nullptr; + if (out_java_indices != nullptr) { + jsize out_indices_len = env->GetArrayLength(out_java_indices); + if (out_indices_len > attrs_len) { + out_indices = + reinterpret_cast(env->GetPrimitiveArrayCritical(out_java_indices, nullptr)); + if (out_indices == nullptr) { + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + env->ReleasePrimitiveArrayCritical(out_java_values, out_values, JNI_ABORT); return JNI_FALSE; + } } + } - jint* srcValues = (jint*)env->GetPrimitiveArrayCritical(inValues, 0); - const jsize NSV = srcValues == NULL ? 0 : env->GetArrayLength(inValues); + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + ResXMLParser* xml_parser = reinterpret_cast(xml_parser_ptr); - jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0); - if (baseDest == NULL) { - env->ReleasePrimitiveArrayCritical(attrs, src, 0); - return JNI_FALSE; - } - - jint* indices = NULL; - if (outIndices != NULL) { - if (env->GetArrayLength(outIndices) > NI) { - indices = (jint*)env->GetPrimitiveArrayCritical(outIndices, 0); - } - } + bool result = RetrieveAttributes(assetmanager.get(), xml_parser, + reinterpret_cast(attrs), attrs_len, + reinterpret_cast(out_values), + reinterpret_cast(out_indices)); - ResTable::Theme* theme = reinterpret_cast(themeToken); - bool result = ResolveAttrs(theme, defStyleAttr, defStyleRes, - (uint32_t*) srcValues, NSV, - (uint32_t*) src, NI, - (uint32_t*) baseDest, - (uint32_t*) indices); - - if (indices != NULL) { - env->ReleasePrimitiveArrayCritical(outIndices, indices, 0); - } - env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0); - env->ReleasePrimitiveArrayCritical(inValues, srcValues, 0); - env->ReleasePrimitiveArrayCritical(attrs, src, 0); - return result ? JNI_TRUE : JNI_FALSE; + if (out_indices != nullptr) { + env->ReleasePrimitiveArrayCritical(out_java_indices, out_indices, 0); + } + env->ReleasePrimitiveArrayCritical(out_java_values, out_values, 0); + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + return result ? JNI_TRUE : JNI_FALSE; } -static void android_content_AssetManager_applyStyle(JNIEnv* env, jobject, jlong themeToken, - jint defStyleAttr, jint defStyleRes, jlong xmlParserToken, jintArray attrsObj, jint length, - jlong outValuesAddress, jlong outIndicesAddress) { - jint* attrs = env->GetIntArrayElements(attrsObj, 0); - ResTable::Theme* theme = reinterpret_cast(themeToken); - ResXMLParser* xmlParser = reinterpret_cast(xmlParserToken); - uint32_t* outValues = reinterpret_cast(static_cast(outValuesAddress)); - uint32_t* outIndices = reinterpret_cast(static_cast(outIndicesAddress)); - ApplyStyle(theme, xmlParser, defStyleAttr, defStyleRes, - reinterpret_cast(attrs), length, outValues, outIndices); - env->ReleaseIntArrayElements(attrsObj, attrs, JNI_ABORT); +static jlong NativeThemeCreate(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + return reinterpret_cast(assetmanager->NewTheme().release()); } -static jboolean android_content_AssetManager_retrieveAttributes(JNIEnv* env, jobject clazz, - jlong xmlParserToken, - jintArray attrs, - jintArray outValues, - jintArray outIndices) -{ - if (xmlParserToken == 0) { - jniThrowNullPointerException(env, "xmlParserToken"); - return JNI_FALSE; - } - if (attrs == NULL) { - jniThrowNullPointerException(env, "attrs"); - return JNI_FALSE; - } - if (outValues == NULL) { - jniThrowNullPointerException(env, "out values"); - return JNI_FALSE; - } - - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return JNI_FALSE; - } - const ResTable& res(am->getResources()); - ResXMLParser* xmlParser = (ResXMLParser*)xmlParserToken; - - const jsize NI = env->GetArrayLength(attrs); - const jsize NV = env->GetArrayLength(outValues); - if (NV < (NI*STYLE_NUM_ENTRIES)) { - jniThrowException(env, "java/lang/IndexOutOfBoundsException", "out values too small"); - return JNI_FALSE; - } - - jint* src = (jint*)env->GetPrimitiveArrayCritical(attrs, 0); - if (src == NULL) { - return JNI_FALSE; - } - - jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0); - if (baseDest == NULL) { - env->ReleasePrimitiveArrayCritical(attrs, src, 0); - return JNI_FALSE; - } - - jint* indices = NULL; - if (outIndices != NULL) { - if (env->GetArrayLength(outIndices) > NI) { - indices = (jint*)env->GetPrimitiveArrayCritical(outIndices, 0); - } - } - - bool result = RetrieveAttributes(&res, xmlParser, - (uint32_t*) src, NI, - (uint32_t*) baseDest, - (uint32_t*) indices); - - if (indices != NULL) { - env->ReleasePrimitiveArrayCritical(outIndices, indices, 0); - } - env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0); - env->ReleasePrimitiveArrayCritical(attrs, src, 0); - return result ? JNI_TRUE : JNI_FALSE; +static void NativeThemeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong theme_ptr) { + delete reinterpret_cast(theme_ptr); } -static jint android_content_AssetManager_getArraySize(JNIEnv* env, jobject clazz, - jint id) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - const ResTable& res(am->getResources()); - - res.lock(); - const ResTable::bag_entry* defStyleEnt = NULL; - ssize_t bagOff = res.getBagLocked(id, &defStyleEnt); - res.unlock(); - - return static_cast(bagOff); +static void NativeThemeApplyStyle(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr, + jint resid, jboolean force) { + // AssetManager is accessed via the theme, so grab an explicit lock here. + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + Theme* theme = reinterpret_cast(theme_ptr); + CHECK(theme->GetAssetManager() == &(*assetmanager)); + (void) assetmanager; + theme->ApplyStyle(static_cast(resid), force); + + // TODO(adamlesinski): Consider surfacing exception when result is failure. + // CTS currently expects no exceptions from this method. + // std::string error_msg = StringPrintf("Failed to apply style 0x%08x to theme", resid); + // jniThrowException(env, "java/lang/IllegalArgumentException", error_msg.c_str()); } -static jint android_content_AssetManager_retrieveArray(JNIEnv* env, jobject clazz, - jint id, - jintArray outValues) -{ - if (outValues == NULL) { - jniThrowNullPointerException(env, "out values"); - return JNI_FALSE; - } - - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return JNI_FALSE; - } - const ResTable& res(am->getResources()); - ResTable_config config; - Res_value value; - ssize_t block; - - const jsize NV = env->GetArrayLength(outValues); - - jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0); - jint* dest = baseDest; - if (dest == NULL) { - jniThrowException(env, "java/lang/OutOfMemoryError", ""); - return JNI_FALSE; - } - - // Now lock down the resource object and start pulling stuff from it. - res.lock(); - - const ResTable::bag_entry* arrayEnt = NULL; - uint32_t arrayTypeSetFlags = 0; - ssize_t bagOff = res.getBagLocked(id, &arrayEnt, &arrayTypeSetFlags); - const ResTable::bag_entry* endArrayEnt = arrayEnt + - (bagOff >= 0 ? bagOff : 0); - - int i = 0; - uint32_t typeSetFlags; - while (i < NV && arrayEnt < endArrayEnt) { - block = arrayEnt->stringBlock; - typeSetFlags = arrayTypeSetFlags; - config.density = 0; - value = arrayEnt->map.value; - - uint32_t resid = 0; - if (value.dataType != Res_value::TYPE_NULL) { - // Take care of resolving the found resource to its final value. - //printf("Resolving attribute reference\n"); - ssize_t newBlock = res.resolveReference(&value, block, &resid, - &typeSetFlags, &config); - if (kThrowOnBadId) { - if (newBlock == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return JNI_FALSE; - } - } - if (newBlock >= 0) block = newBlock; - } - - // Deal with the special @null value -- it turns back to TYPE_NULL. - if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { - value.dataType = Res_value::TYPE_NULL; - value.data = Res_value::DATA_NULL_UNDEFINED; - } - - //printf("Attribute 0x%08x: final type=0x%x, data=0x%08x\n", curIdent, value.dataType, value.data); - - // Write the final value back to Java. - dest[STYLE_TYPE] = value.dataType; - dest[STYLE_DATA] = value.data; - dest[STYLE_ASSET_COOKIE] = reinterpret_cast(res.getTableCookie(block)); - dest[STYLE_RESOURCE_ID] = resid; - dest[STYLE_CHANGING_CONFIGURATIONS] = typeSetFlags; - dest[STYLE_DENSITY] = config.density; - dest += STYLE_NUM_ENTRIES; - i+= STYLE_NUM_ENTRIES; - arrayEnt++; - } - - i /= STYLE_NUM_ENTRIES; - - res.unlock(); - - env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0); - - return i; +static void NativeThemeCopy(JNIEnv* env, jclass /*clazz*/, jlong dst_theme_ptr, + jlong src_theme_ptr) { + Theme* dst_theme = reinterpret_cast(dst_theme_ptr); + Theme* src_theme = reinterpret_cast(src_theme_ptr); + if (!dst_theme->SetTo(*src_theme)) { + jniThrowException(env, "java/lang/IllegalArgumentException", + "Themes are from different AssetManagers"); + } } -static jlong android_content_AssetManager_openXmlAssetNative(JNIEnv* env, jobject clazz, - jint cookie, - jstring fileName) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - - ALOGV("openXmlAsset in %p (Java object %p)\n", am, clazz); - - ScopedUtfChars fileName8(env, fileName); - if (fileName8.c_str() == NULL) { - return 0; - } - - int32_t assetCookie = static_cast(cookie); - Asset* a = assetCookie - ? am->openNonAsset(assetCookie, fileName8.c_str(), Asset::ACCESS_BUFFER) - : am->openNonAsset(fileName8.c_str(), Asset::ACCESS_BUFFER, &assetCookie); - - if (a == NULL) { - jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); - return 0; - } - - const DynamicRefTable* dynamicRefTable = - am->getResources().getDynamicRefTableForCookie(assetCookie); - ResXMLTree* block = new ResXMLTree(dynamicRefTable); - status_t err = block->setTo(a->getBuffer(true), a->getLength(), true); - a->close(); - delete a; - - if (err != NO_ERROR) { - jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file"); - return 0; - } - - return reinterpret_cast(block); +static void NativeThemeClear(JNIEnv* /*env*/, jclass /*clazz*/, jlong theme_ptr) { + reinterpret_cast(theme_ptr)->Clear(); } -static jintArray android_content_AssetManager_getArrayStringInfo(JNIEnv* env, jobject clazz, - jint arrayResId) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - const ResTable& res(am->getResources()); - - const ResTable::bag_entry* startOfBag; - const ssize_t N = res.lockBag(arrayResId, &startOfBag); - if (N < 0) { - return NULL; - } - - jintArray array = env->NewIntArray(N * 2); - if (array == NULL) { - res.unlockBag(startOfBag); - return NULL; - } - - Res_value value; - const ResTable::bag_entry* bag = startOfBag; - for (size_t i = 0, j = 0; ((ssize_t)i)map.value; - - // Take care of resolving the found resource to its final value. - stringBlock = res.resolveReference(&value, bag->stringBlock, NULL); - if (value.dataType == Res_value::TYPE_STRING) { - stringIndex = value.data; - } - - if (kThrowOnBadId) { - if (stringBlock == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return array; - } - } - - //todo: It might be faster to allocate a C array to contain - // the blocknums and indices, put them in there and then - // do just one SetIntArrayRegion() - env->SetIntArrayRegion(array, j, 1, &stringBlock); - env->SetIntArrayRegion(array, j + 1, 1, &stringIndex); - j = j + 2; - } - res.unlockBag(startOfBag); - return array; +static jint NativeThemeGetAttributeValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr, + jint resid, jobject typed_value, + jboolean resolve_references) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + Theme* theme = reinterpret_cast(theme_ptr); + CHECK(theme->GetAssetManager() == &(*assetmanager)); + (void) assetmanager; + + Res_value value; + uint32_t flags; + ApkAssetsCookie cookie = theme->GetAttribute(static_cast(resid), &value, &flags); + if (cookie == kInvalidCookie) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + + uint32_t ref = 0u; + if (resolve_references) { + ResTable_config selected_config; + cookie = + theme->GetAssetManager()->ResolveReference(cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + } + return CopyValue(env, cookie, value, ref, flags, nullptr, typed_value); } -static jobjectArray android_content_AssetManager_getArrayStringResource(JNIEnv* env, jobject clazz, - jint arrayResId) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - const ResTable& res(am->getResources()); - - const ResTable::bag_entry* startOfBag; - const ssize_t N = res.lockBag(arrayResId, &startOfBag); - if (N < 0) { - return NULL; - } - - jobjectArray array = env->NewObjectArray(N, g_stringClass, NULL); - if (env->ExceptionCheck()) { - res.unlockBag(startOfBag); - return NULL; - } - - Res_value value; - const ResTable::bag_entry* bag = startOfBag; - size_t strLen = 0; - for (size_t i=0; ((ssize_t)i)map.value; - jstring str = NULL; - - // Take care of resolving the found resource to its final value. - ssize_t block = res.resolveReference(&value, bag->stringBlock, NULL); - if (kThrowOnBadId) { - if (block == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return array; - } - } - if (value.dataType == Res_value::TYPE_STRING) { - const ResStringPool* pool = res.getTableStringBlock(block); - const char* str8 = pool->string8At(value.data, &strLen); - if (str8 != NULL) { - str = env->NewStringUTF(str8); - } else { - const char16_t* str16 = pool->stringAt(value.data, &strLen); - str = env->NewString(reinterpret_cast(str16), - strLen); - } - - // If one of our NewString{UTF} calls failed due to memory, an - // exception will be pending. - if (env->ExceptionCheck()) { - res.unlockBag(startOfBag); - return NULL; - } - - env->SetObjectArrayElement(array, i, str); - - // str is not NULL at that point, otherwise ExceptionCheck would have been true. - // If we have a large amount of strings in our array, we might - // overflow the local reference table of the VM. - env->DeleteLocalRef(str); - } - } - res.unlockBag(startOfBag); - return array; +static void NativeThemeDump(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr, jlong theme_ptr, + jint priority, jstring tag, jstring prefix) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + Theme* theme = reinterpret_cast(theme_ptr); + CHECK(theme->GetAssetManager() == &(*assetmanager)); + (void) assetmanager; + (void) theme; + (void) priority; + (void) tag; + (void) prefix; } -static jintArray android_content_AssetManager_getArrayIntResource(JNIEnv* env, jobject clazz, - jint arrayResId) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - const ResTable& res(am->getResources()); - - const ResTable::bag_entry* startOfBag; - const ssize_t N = res.lockBag(arrayResId, &startOfBag); - if (N < 0) { - return NULL; - } - - jintArray array = env->NewIntArray(N); - if (array == NULL) { - res.unlockBag(startOfBag); - return NULL; - } - - Res_value value; - const ResTable::bag_entry* bag = startOfBag; - for (size_t i=0; ((ssize_t)i)map.value; - - // Take care of resolving the found resource to its final value. - ssize_t block = res.resolveReference(&value, bag->stringBlock, NULL); - if (kThrowOnBadId) { - if (block == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return array; - } - } - if (value.dataType >= Res_value::TYPE_FIRST_INT - && value.dataType <= Res_value::TYPE_LAST_INT) { - int intVal = value.data; - env->SetIntArrayRegion(array, i, 1, &intVal); - } - } - res.unlockBag(startOfBag); - return array; +static jint NativeThemeGetChangingConfigurations(JNIEnv* /*env*/, jclass /*clazz*/, + jlong theme_ptr) { + Theme* theme = reinterpret_cast(theme_ptr); + return static_cast(theme->GetChangingConfigurations()); } -static jintArray android_content_AssetManager_getStyleAttributes(JNIEnv* env, jobject clazz, - jint styleId) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - const ResTable& res(am->getResources()); - - const ResTable::bag_entry* startOfBag; - const ssize_t N = res.lockBag(styleId, &startOfBag); - if (N < 0) { - return NULL; - } - - jintArray array = env->NewIntArray(N); - if (array == NULL) { - res.unlockBag(startOfBag); - return NULL; - } +static void NativeAssetDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) { + delete reinterpret_cast(asset_ptr); +} - const ResTable::bag_entry* bag = startOfBag; - for (size_t i=0; ((ssize_t)i)map.name.ident; - env->SetIntArrayRegion(array, i, 1, &resourceId); - } - res.unlockBag(startOfBag); - return array; +static jint NativeAssetReadChar(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) { + Asset* asset = reinterpret_cast(asset_ptr); + uint8_t b; + ssize_t res = asset->read(&b, sizeof(b)); + return res == sizeof(b) ? static_cast(b) : -1; } -static void android_content_AssetManager_init(JNIEnv* env, jobject clazz, jboolean isSystem) -{ - if (isSystem) { - verifySystemIdmaps(); - } - AssetManager* am = new AssetManager(); - if (am == NULL) { - jniThrowException(env, "java/lang/OutOfMemoryError", ""); - return; - } +static jint NativeAssetRead(JNIEnv* env, jclass /*clazz*/, jlong asset_ptr, jbyteArray java_buffer, + jint offset, jint len) { + if (len == 0) { + return 0; + } - am->addDefaultAssets(); + jsize buffer_len = env->GetArrayLength(java_buffer); + if (offset < 0 || offset >= buffer_len || len < 0 || len > buffer_len || + offset > buffer_len - len) { + jniThrowException(env, "java/lang/IndexOutOfBoundsException", ""); + return -1; + } - ALOGV("Created AssetManager %p for Java object %p\n", am, clazz); - env->SetLongField(clazz, gAssetManagerOffsets.mObject, reinterpret_cast(am)); -} + ScopedByteArrayRW byte_array(env, java_buffer); + if (byte_array.get() == nullptr) { + return -1; + } -static void android_content_AssetManager_destroy(JNIEnv* env, jobject clazz) -{ - AssetManager* am = (AssetManager*) - (env->GetLongField(clazz, gAssetManagerOffsets.mObject)); - ALOGV("Destroying AssetManager %p for Java object %p\n", am, clazz); - if (am != NULL) { - delete am; - env->SetLongField(clazz, gAssetManagerOffsets.mObject, 0); - } + Asset* asset = reinterpret_cast(asset_ptr); + ssize_t res = asset->read(byte_array.get() + offset, len); + if (res < 0) { + jniThrowException(env, "java/io/IOException", ""); + return -1; + } + return res > 0 ? static_cast(res) : -1; } -static jint android_content_AssetManager_getGlobalAssetCount(JNIEnv* env, jobject clazz) -{ - return Asset::getGlobalCount(); +static jlong NativeAssetSeek(JNIEnv* env, jclass /*clazz*/, jlong asset_ptr, jlong offset, + jint whence) { + Asset* asset = reinterpret_cast(asset_ptr); + return static_cast(asset->seek( + static_cast(offset), (whence > 0 ? SEEK_END : (whence < 0 ? SEEK_SET : SEEK_CUR)))); } -static jobject android_content_AssetManager_getAssetAllocations(JNIEnv* env, jobject clazz) -{ - String8 alloc = Asset::getAssetAllocations(); - if (alloc.length() <= 0) { - return NULL; - } - - jstring str = env->NewStringUTF(alloc.string()); - return str; +static jlong NativeAssetGetLength(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) { + Asset* asset = reinterpret_cast(asset_ptr); + return static_cast(asset->getLength()); } -static jint android_content_AssetManager_getGlobalAssetManagerCount(JNIEnv* env, jobject clazz) -{ - return AssetManager::getGlobalCount(); +static jlong NativeAssetGetRemainingLength(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) { + Asset* asset = reinterpret_cast(asset_ptr); + return static_cast(asset->getRemainingLength()); } // ---------------------------------------------------------------------------- -/* - * JNI registration. - */ +// JNI registration. static const JNINativeMethod gAssetManagerMethods[] = { - /* name, signature, funcPtr */ - - // Basic asset stuff. - { "openAsset", "(Ljava/lang/String;I)J", - (void*) android_content_AssetManager_openAsset }, - { "openAssetFd", "(Ljava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", - (void*) android_content_AssetManager_openAssetFd }, - { "openNonAssetNative", "(ILjava/lang/String;I)J", - (void*) android_content_AssetManager_openNonAssetNative }, - { "openNonAssetFdNative", "(ILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", - (void*) android_content_AssetManager_openNonAssetFdNative }, - { "list", "(Ljava/lang/String;)[Ljava/lang/String;", - (void*) android_content_AssetManager_list }, - { "destroyAsset", "(J)V", - (void*) android_content_AssetManager_destroyAsset }, - { "readAssetChar", "(J)I", - (void*) android_content_AssetManager_readAssetChar }, - { "readAsset", "(J[BII)I", - (void*) android_content_AssetManager_readAsset }, - { "seekAsset", "(JJI)J", - (void*) android_content_AssetManager_seekAsset }, - { "getAssetLength", "(J)J", - (void*) android_content_AssetManager_getAssetLength }, - { "getAssetRemainingLength", "(J)J", - (void*) android_content_AssetManager_getAssetRemainingLength }, - { "addAssetPathNative", "(Ljava/lang/String;Z)I", - (void*) android_content_AssetManager_addAssetPath }, - { "addAssetFdNative", "(Ljava/io/FileDescriptor;Ljava/lang/String;Z)I", - (void*) android_content_AssetManager_addAssetFd }, - { "addOverlayPathNative", "(Ljava/lang/String;)I", - (void*) android_content_AssetManager_addOverlayPath }, - { "isUpToDate", "()Z", - (void*) android_content_AssetManager_isUpToDate }, - - // Resources. - { "getLocales", "()[Ljava/lang/String;", - (void*) android_content_AssetManager_getLocales }, - { "getNonSystemLocales", "()[Ljava/lang/String;", - (void*) android_content_AssetManager_getNonSystemLocales }, - { "getSizeConfigurations", "()[Landroid/content/res/Configuration;", - (void*) android_content_AssetManager_getSizeConfigurations }, - { "setConfiguration", "(IILjava/lang/String;IIIIIIIIIIIIIII)V", - (void*) android_content_AssetManager_setConfiguration }, - { "getResourceIdentifier","(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I", - (void*) android_content_AssetManager_getResourceIdentifier }, - { "getResourceName","(I)Ljava/lang/String;", - (void*) android_content_AssetManager_getResourceName }, - { "getResourcePackageName","(I)Ljava/lang/String;", - (void*) android_content_AssetManager_getResourcePackageName }, - { "getResourceTypeName","(I)Ljava/lang/String;", - (void*) android_content_AssetManager_getResourceTypeName }, - { "getResourceEntryName","(I)Ljava/lang/String;", - (void*) android_content_AssetManager_getResourceEntryName }, - { "loadResourceValue","(ISLandroid/util/TypedValue;Z)I", - (void*) android_content_AssetManager_loadResourceValue }, - { "loadResourceBagValue","(IILandroid/util/TypedValue;Z)I", - (void*) android_content_AssetManager_loadResourceBagValue }, - { "getStringBlockCount","()I", - (void*) android_content_AssetManager_getStringBlockCount }, - { "getNativeStringBlock","(I)J", - (void*) android_content_AssetManager_getNativeStringBlock }, - { "getCookieName","(I)Ljava/lang/String;", - (void*) android_content_AssetManager_getCookieName }, - { "getAssignedPackageIdentifiers","()Landroid/util/SparseArray;", - (void*) android_content_AssetManager_getAssignedPackageIdentifiers }, - - // Themes. - { "newTheme", "()J", - (void*) android_content_AssetManager_newTheme }, - { "deleteTheme", "(J)V", - (void*) android_content_AssetManager_deleteTheme }, - { "applyThemeStyle", "(JIZ)V", - (void*) android_content_AssetManager_applyThemeStyle }, - { "copyTheme", "(JJ)V", - (void*) android_content_AssetManager_copyTheme }, - { "clearTheme", "(J)V", - (void*) android_content_AssetManager_clearTheme }, - { "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[IIJJ)V", - (void*) android_content_AssetManager_applyStyle }, - { "resolveAttrs","(JII[I[I[I[I)Z", - (void*) android_content_AssetManager_resolveAttrs }, - { "retrieveAttributes","(J[I[I[I)Z", - (void*) android_content_AssetManager_retrieveAttributes }, - { "getArraySize","(I)I", - (void*) android_content_AssetManager_getArraySize }, - { "retrieveArray","(I[I)I", - (void*) android_content_AssetManager_retrieveArray }, - - // XML files. - { "openXmlAssetNative", "(ILjava/lang/String;)J", - (void*) android_content_AssetManager_openXmlAssetNative }, - - // Arrays. - { "getArrayStringResource","(I)[Ljava/lang/String;", - (void*) android_content_AssetManager_getArrayStringResource }, - { "getArrayStringInfo","(I)[I", - (void*) android_content_AssetManager_getArrayStringInfo }, - { "getArrayIntResource","(I)[I", - (void*) android_content_AssetManager_getArrayIntResource }, - { "getStyleAttributes","(I)[I", - (void*) android_content_AssetManager_getStyleAttributes }, - - // Bookkeeping. - { "init", "(Z)V", - (void*) android_content_AssetManager_init }, - { "destroy", "()V", - (void*) android_content_AssetManager_destroy }, - { "getGlobalAssetCount", "()I", - (void*) android_content_AssetManager_getGlobalAssetCount }, - { "getAssetAllocations", "()Ljava/lang/String;", - (void*) android_content_AssetManager_getAssetAllocations }, - { "getGlobalAssetManagerCount", "()I", - (void*) android_content_AssetManager_getGlobalAssetManagerCount }, + // AssetManager setup methods. + {"nativeCreate", "()J", (void*)NativeCreate}, + {"nativeDestroy", "(J)V", (void*)NativeDestroy}, + {"nativeSetApkAssets", "(J[Landroid/content/res/ApkAssets;Z)V", (void*)NativeSetApkAssets}, + {"nativeSetConfiguration", "(JIILjava/lang/String;IIIIIIIIIIIIIII)V", + (void*)NativeSetConfiguration}, + {"nativeGetAssignedPackageIdentifiers", "(J)Landroid/util/SparseArray;", + (void*)NativeGetAssignedPackageIdentifiers}, + + // AssetManager file methods. + {"nativeList", "(JLjava/lang/String;)[Ljava/lang/String;", (void*)NativeList}, + {"nativeOpenAsset", "(JLjava/lang/String;I)J", (void*)NativeOpenAsset}, + {"nativeOpenAssetFd", "(JLjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", + (void*)NativeOpenAssetFd}, + {"nativeOpenNonAsset", "(JILjava/lang/String;I)J", (void*)NativeOpenNonAsset}, + {"nativeOpenNonAssetFd", "(JILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", + (void*)NativeOpenNonAssetFd}, + {"nativeOpenXmlAsset", "(JILjava/lang/String;)J", (void*)NativeOpenXmlAsset}, + + // AssetManager resource methods. + {"nativeGetResourceValue", "(JISLandroid/util/TypedValue;Z)I", (void*)NativeGetResourceValue}, + {"nativeGetResourceBagValue", "(JIILandroid/util/TypedValue;)I", + (void*)NativeGetResourceBagValue}, + {"nativeGetStyleAttributes", "(JI)[I", (void*)NativeGetStyleAttributes}, + {"nativeGetResourceStringArray", "(JI)[Ljava/lang/String;", + (void*)NativeGetResourceStringArray}, + {"nativeGetResourceStringArrayInfo", "(JI)[I", (void*)NativeGetResourceStringArrayInfo}, + {"nativeGetResourceIntArray", "(JI)[I", (void*)NativeGetResourceIntArray}, + {"nativeGetResourceArraySize", "(JI)I", (void*)NativeGetResourceArraySize}, + {"nativeGetResourceArray", "(JI[I)I", (void*)NativeGetResourceArray}, + + // AssetManager resource name/ID methods. + {"nativeGetResourceIdentifier", "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I", + (void*)NativeGetResourceIdentifier}, + {"nativeGetResourceName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceName}, + {"nativeGetResourcePackageName", "(JI)Ljava/lang/String;", (void*)NativeGetResourcePackageName}, + {"nativeGetResourceTypeName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceTypeName}, + {"nativeGetResourceEntryName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceEntryName}, + {"nativeGetLocales", "(JZ)[Ljava/lang/String;", (void*)NativeGetLocales}, + {"nativeGetSizeConfigurations", "(J)[Landroid/content/res/Configuration;", + (void*)NativeGetSizeConfigurations}, + + // Style attribute related methods. + {"nativeApplyStyle", "(JJIIJ[IJJ)V", (void*)NativeApplyStyle}, + {"nativeResolveAttrs", "(JJII[I[I[I[I)Z", (void*)NativeResolveAttrs}, + {"nativeRetrieveAttributes", "(JJ[I[I[I)Z", (void*)NativeRetrieveAttributes}, + + // Theme related methods. + {"nativeThemeCreate", "(J)J", (void*)NativeThemeCreate}, + {"nativeThemeDestroy", "(J)V", (void*)NativeThemeDestroy}, + {"nativeThemeApplyStyle", "(JJIZ)V", (void*)NativeThemeApplyStyle}, + {"nativeThemeCopy", "(JJ)V", (void*)NativeThemeCopy}, + {"nativeThemeClear", "(J)V", (void*)NativeThemeClear}, + {"nativeThemeGetAttributeValue", "(JJILandroid/util/TypedValue;Z)I", + (void*)NativeThemeGetAttributeValue}, + {"nativeThemeDump", "(JJILjava/lang/String;Ljava/lang/String;)V", (void*)NativeThemeDump}, + {"nativeThemeGetChangingConfigurations", "(J)I", (void*)NativeThemeGetChangingConfigurations}, + + // AssetInputStream methods. + {"nativeAssetDestroy", "(J)V", (void*)NativeAssetDestroy}, + {"nativeAssetReadChar", "(J)I", (void*)NativeAssetReadChar}, + {"nativeAssetRead", "(J[BII)I", (void*)NativeAssetRead}, + {"nativeAssetSeek", "(JJI)J", (void*)NativeAssetSeek}, + {"nativeAssetGetLength", "(J)J", (void*)NativeAssetGetLength}, + {"nativeAssetGetRemainingLength", "(J)J", (void*)NativeAssetGetRemainingLength}, + + // System/idmap related methods. + {"nativeVerifySystemIdmaps", "()V", (void*)NativeVerifySystemIdmaps}, + + // Global management/debug methods. + {"getGlobalAssetCount", "()I", (void*)NativeGetGlobalAssetCount}, + {"getAssetAllocations", "()Ljava/lang/String;", (void*)NativeGetAssetAllocations}, + {"getGlobalAssetManagerCount", "()I", (void*)NativeGetGlobalAssetManagerCount}, }; -int register_android_content_AssetManager(JNIEnv* env) -{ - jclass typedValue = FindClassOrDie(env, "android/util/TypedValue"); - gTypedValueOffsets.mType = GetFieldIDOrDie(env, typedValue, "type", "I"); - gTypedValueOffsets.mData = GetFieldIDOrDie(env, typedValue, "data", "I"); - gTypedValueOffsets.mString = GetFieldIDOrDie(env, typedValue, "string", - "Ljava/lang/CharSequence;"); - gTypedValueOffsets.mAssetCookie = GetFieldIDOrDie(env, typedValue, "assetCookie", "I"); - gTypedValueOffsets.mResourceId = GetFieldIDOrDie(env, typedValue, "resourceId", "I"); - gTypedValueOffsets.mChangingConfigurations = GetFieldIDOrDie(env, typedValue, - "changingConfigurations", "I"); - gTypedValueOffsets.mDensity = GetFieldIDOrDie(env, typedValue, "density", "I"); - - jclass assetFd = FindClassOrDie(env, "android/content/res/AssetFileDescriptor"); - gAssetFileDescriptorOffsets.mFd = GetFieldIDOrDie(env, assetFd, "mFd", - "Landroid/os/ParcelFileDescriptor;"); - gAssetFileDescriptorOffsets.mStartOffset = GetFieldIDOrDie(env, assetFd, "mStartOffset", "J"); - gAssetFileDescriptorOffsets.mLength = GetFieldIDOrDie(env, assetFd, "mLength", "J"); - - jclass assetManager = FindClassOrDie(env, "android/content/res/AssetManager"); - gAssetManagerOffsets.mObject = GetFieldIDOrDie(env, assetManager, "mObject", "J"); - - jclass stringClass = FindClassOrDie(env, "java/lang/String"); - g_stringClass = MakeGlobalRefOrDie(env, stringClass); - - jclass sparseArrayClass = FindClassOrDie(env, "android/util/SparseArray"); - gSparseArrayOffsets.classObject = MakeGlobalRefOrDie(env, sparseArrayClass); - gSparseArrayOffsets.constructor = GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, - "", "()V"); - gSparseArrayOffsets.put = GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "put", - "(ILjava/lang/Object;)V"); - - jclass configurationClass = FindClassOrDie(env, "android/content/res/Configuration"); - gConfigurationOffsets.classObject = MakeGlobalRefOrDie(env, configurationClass); - gConfigurationOffsets.constructor = GetMethodIDOrDie(env, configurationClass, - "", "()V"); - gConfigurationOffsets.mSmallestScreenWidthDpOffset = GetFieldIDOrDie(env, configurationClass, - "smallestScreenWidthDp", "I"); - gConfigurationOffsets.mScreenWidthDpOffset = GetFieldIDOrDie(env, configurationClass, - "screenWidthDp", "I"); - gConfigurationOffsets.mScreenHeightDpOffset = GetFieldIDOrDie(env, configurationClass, - "screenHeightDp", "I"); - - return RegisterMethodsOrDie(env, "android/content/res/AssetManager", gAssetManagerMethods, - NELEM(gAssetManagerMethods)); +int register_android_content_AssetManager(JNIEnv* env) { + jclass apk_assets_class = FindClassOrDie(env, "android/content/res/ApkAssets"); + gApkAssetsFields.native_ptr = GetFieldIDOrDie(env, apk_assets_class, "mNativePtr", "J"); + + jclass typedValue = FindClassOrDie(env, "android/util/TypedValue"); + gTypedValueOffsets.mType = GetFieldIDOrDie(env, typedValue, "type", "I"); + gTypedValueOffsets.mData = GetFieldIDOrDie(env, typedValue, "data", "I"); + gTypedValueOffsets.mString = + GetFieldIDOrDie(env, typedValue, "string", "Ljava/lang/CharSequence;"); + gTypedValueOffsets.mAssetCookie = GetFieldIDOrDie(env, typedValue, "assetCookie", "I"); + gTypedValueOffsets.mResourceId = GetFieldIDOrDie(env, typedValue, "resourceId", "I"); + gTypedValueOffsets.mChangingConfigurations = + GetFieldIDOrDie(env, typedValue, "changingConfigurations", "I"); + gTypedValueOffsets.mDensity = GetFieldIDOrDie(env, typedValue, "density", "I"); + + jclass assetFd = FindClassOrDie(env, "android/content/res/AssetFileDescriptor"); + gAssetFileDescriptorOffsets.mFd = + GetFieldIDOrDie(env, assetFd, "mFd", "Landroid/os/ParcelFileDescriptor;"); + gAssetFileDescriptorOffsets.mStartOffset = GetFieldIDOrDie(env, assetFd, "mStartOffset", "J"); + gAssetFileDescriptorOffsets.mLength = GetFieldIDOrDie(env, assetFd, "mLength", "J"); + + jclass assetManager = FindClassOrDie(env, "android/content/res/AssetManager"); + gAssetManagerOffsets.mObject = GetFieldIDOrDie(env, assetManager, "mObject", "J"); + + jclass stringClass = FindClassOrDie(env, "java/lang/String"); + g_stringClass = MakeGlobalRefOrDie(env, stringClass); + + jclass sparseArrayClass = FindClassOrDie(env, "android/util/SparseArray"); + gSparseArrayOffsets.classObject = MakeGlobalRefOrDie(env, sparseArrayClass); + gSparseArrayOffsets.constructor = + GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "", "()V"); + gSparseArrayOffsets.put = + GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "put", "(ILjava/lang/Object;)V"); + + jclass configurationClass = FindClassOrDie(env, "android/content/res/Configuration"); + gConfigurationOffsets.classObject = MakeGlobalRefOrDie(env, configurationClass); + gConfigurationOffsets.constructor = GetMethodIDOrDie(env, configurationClass, "", "()V"); + gConfigurationOffsets.mSmallestScreenWidthDpOffset = + GetFieldIDOrDie(env, configurationClass, "smallestScreenWidthDp", "I"); + gConfigurationOffsets.mScreenWidthDpOffset = + GetFieldIDOrDie(env, configurationClass, "screenWidthDp", "I"); + gConfigurationOffsets.mScreenHeightDpOffset = + GetFieldIDOrDie(env, configurationClass, "screenHeightDp", "I"); + + return RegisterMethodsOrDie(env, "android/content/res/AssetManager", gAssetManagerMethods, + NELEM(gAssetManagerMethods)); } }; // namespace android diff --git a/core/jni/include/android_runtime/android_util_AssetManager.h b/core/jni/include/android_runtime/android_util_AssetManager.h index 8dd933707a6..2c1e3579eb9 100644 --- a/core/jni/include/android_runtime/android_util_AssetManager.h +++ b/core/jni/include/android_runtime/android_util_AssetManager.h @@ -14,17 +14,20 @@ * limitations under the License. */ -#ifndef android_util_AssetManager_H -#define android_util_AssetManager_H +#ifndef ANDROID_RUNTIME_ASSETMANAGER_H +#define ANDROID_RUNTIME_ASSETMANAGER_H -#include +#include "androidfw/AssetManager2.h" +#include "androidfw/MutexGuard.h" #include "jni.h" namespace android { -extern AssetManager* assetManagerForJavaObject(JNIEnv* env, jobject assetMgr); +extern AAssetManager* NdkAssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager); +extern Guarded* AssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager); +extern Guarded* AssetManagerForNdkAssetManager(AAssetManager* assetmanager); -} +} // namespace android -#endif +#endif // ANDROID_RUNTIME_ASSETMANAGER_H diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index 415d3e36adf..2fc8e952707 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -265,8 +265,6 @@ std::unique_ptr AssetManager2::OpenNonAsset(const std::string& filename, ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_override, bool stop_at_first_match, FindEntryResult* out_entry) { - ATRACE_CALL(); - // Might use this if density_override != 0. ResTable_config density_override_config; @@ -429,9 +427,7 @@ ApkAssetsCookie AssetManager2::ResolveReference(ApkAssetsCookie cookie, Res_valu for (size_t iteration = 0u; in_out_value->dataType == Res_value::TYPE_REFERENCE && in_out_value->data != 0u && iteration < kMaxIterations; iteration++) { - if (out_last_reference != nullptr) { - *out_last_reference = in_out_value->data; - } + *out_last_reference = in_out_value->data; uint32_t new_flags = 0u; cookie = GetResource(in_out_value->data, true /*may_be_bag*/, 0u /*density_override*/, in_out_value, in_out_selected_config, &new_flags); diff --git a/libs/androidfw/AttributeResolution.cpp b/libs/androidfw/AttributeResolution.cpp index 60e3845d98a..f912af4f719 100644 --- a/libs/androidfw/AttributeResolution.cpp +++ b/libs/androidfw/AttributeResolution.cpp @@ -20,13 +20,18 @@ #include +#include "androidfw/AssetManager2.h" #include "androidfw/AttributeFinder.h" -#include "androidfw/ResourceTypes.h" constexpr bool kDebugStyles = false; namespace android { +// Java asset cookies have 0 as an invalid cookie, but TypedArray expects < 0. +static uint32_t ApkAssetsCookieToJavaCookie(ApkAssetsCookie cookie) { + return cookie != kInvalidCookie ? static_cast(cookie + 1) : static_cast(-1); +} + class XmlAttributeFinder : public BackTrackingAttributeFinder { public: @@ -44,58 +49,53 @@ class XmlAttributeFinder }; class BagAttributeFinder - : public BackTrackingAttributeFinder { + : public BackTrackingAttributeFinder { public: - BagAttributeFinder(const ResTable::bag_entry* start, - const ResTable::bag_entry* end) - : BackTrackingAttributeFinder(start, end) {} + BagAttributeFinder(const ResolvedBag* bag) + : BackTrackingAttributeFinder(bag != nullptr ? bag->entries : nullptr, + bag != nullptr ? bag->entries + bag->entry_count : nullptr) { + } - inline uint32_t GetAttribute(const ResTable::bag_entry* entry) const { - return entry->map.name.ident; + inline uint32_t GetAttribute(const ResolvedBag::Entry* entry) const { + return entry->key; } }; -bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, - uint32_t def_style_res, uint32_t* src_values, - size_t src_values_length, uint32_t* attrs, - size_t attrs_length, uint32_t* out_values, - uint32_t* out_indices) { +bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, + uint32_t* src_values, size_t src_values_length, uint32_t* attrs, + size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) { if (kDebugStyles) { ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x", theme, def_style_attr, def_style_res); } - const ResTable& res = theme->getResTable(); + AssetManager2* assetmanager = theme->GetAssetManager(); ResTable_config config; Res_value value; int indices_idx = 0; // Load default style from attribute, if specified... - uint32_t def_style_bag_type_set_flags = 0; + uint32_t def_style_flags = 0u; if (def_style_attr != 0) { Res_value value; - if (theme->getAttribute(def_style_attr, &value, &def_style_bag_type_set_flags) >= 0) { + if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) { if (value.dataType == Res_value::TYPE_REFERENCE) { def_style_res = value.data; } } } - // Now lock down the resource object and start pulling stuff from it. - res.lock(); - // Retrieve the default style bag, if requested. - const ResTable::bag_entry* def_style_start = nullptr; - uint32_t def_style_type_set_flags = 0; - ssize_t bag_off = def_style_res != 0 - ? res.getBagLocked(def_style_res, &def_style_start, - &def_style_type_set_flags) - : -1; - def_style_type_set_flags |= def_style_bag_type_set_flags; - const ResTable::bag_entry* const def_style_end = - def_style_start + (bag_off >= 0 ? bag_off : 0); - BagAttributeFinder def_style_attr_finder(def_style_start, def_style_end); + const ResolvedBag* default_style_bag = nullptr; + if (def_style_res != 0) { + default_style_bag = assetmanager->GetBag(def_style_res); + if (default_style_bag != nullptr) { + def_style_flags |= default_style_bag->type_spec_flags; + } + } + + BagAttributeFinder def_style_attr_finder(default_style_bag); // Now iterate through all of the attributes that the client has requested, // filling in each with whatever data we can find. @@ -106,7 +106,7 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident); } - ssize_t block = -1; + ApkAssetsCookie cookie = kInvalidCookie; uint32_t type_set_flags = 0; value.dataType = Res_value::TYPE_NULL; @@ -122,15 +122,14 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, value.dataType = Res_value::TYPE_ATTRIBUTE; value.data = src_values[ii]; if (kDebugStyles) { - ALOGI("-> From values: type=0x%x, data=0x%08x", value.dataType, - value.data); + ALOGI("-> From values: type=0x%x, data=0x%08x", value.dataType, value.data); } } else { - const ResTable::bag_entry* const def_style_entry = def_style_attr_finder.Find(cur_ident); - if (def_style_entry != def_style_end) { - block = def_style_entry->stringBlock; - type_set_flags = def_style_type_set_flags; - value = def_style_entry->map.value; + const ResolvedBag::Entry* const entry = def_style_attr_finder.Find(cur_ident); + if (entry != def_style_attr_finder.end()) { + cookie = entry->cookie; + type_set_flags = def_style_flags; + value = entry->value; if (kDebugStyles) { ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data); } @@ -140,22 +139,26 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, uint32_t resid = 0; if (value.dataType != Res_value::TYPE_NULL) { // Take care of resolving the found resource to its final value. - ssize_t new_block = - theme->resolveAttributeReference(&value, block, &resid, &type_set_flags, &config); - if (new_block >= 0) block = new_block; + ApkAssetsCookie new_cookie = + theme->ResolveAttributeReference(cookie, &value, &config, &type_set_flags, &resid); + if (new_cookie != kInvalidCookie) { + cookie = new_cookie; + } if (kDebugStyles) { ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, value.data); } } else if (value.data != Res_value::DATA_NULL_EMPTY) { - // If we still don't have a value for this attribute, try to find - // it in the theme! - ssize_t new_block = theme->getAttribute(cur_ident, &value, &type_set_flags); - if (new_block >= 0) { + // If we still don't have a value for this attribute, try to find it in the theme! + ApkAssetsCookie new_cookie = theme->GetAttribute(cur_ident, &value, &type_set_flags); + if (new_cookie != kInvalidCookie) { if (kDebugStyles) { ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data); } - new_block = res.resolveReference(&value, new_block, &resid, &type_set_flags, &config); - if (new_block >= 0) block = new_block; + new_cookie = + assetmanager->ResolveReference(new_cookie, &value, &config, &type_set_flags, &resid); + if (new_cookie != kInvalidCookie) { + cookie = new_cookie; + } if (kDebugStyles) { ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, value.data); } @@ -169,7 +172,7 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, } value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; - block = -1; + cookie = kInvalidCookie; } if (kDebugStyles) { @@ -179,9 +182,7 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, // Write the final value back to Java. out_values[STYLE_TYPE] = value.dataType; out_values[STYLE_DATA] = value.data; - out_values[STYLE_ASSET_COOKIE] = - block != -1 ? static_cast(res.getTableCookie(block)) - : static_cast(-1); + out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); out_values[STYLE_RESOURCE_ID] = resid; out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; out_values[STYLE_DENSITY] = config.density; @@ -195,90 +196,80 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, out_values += STYLE_NUM_ENTRIES; } - res.unlock(); - if (out_indices != nullptr) { out_indices[0] = indices_idx; } return true; } -void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, - uint32_t def_style_res, const uint32_t* attrs, size_t attrs_length, +void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, + uint32_t def_style_resid, const uint32_t* attrs, size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) { if (kDebugStyles) { - ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p", - theme, def_style_attr, def_style_res, xml_parser); + ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p", theme, + def_style_attr, def_style_resid, xml_parser); } - const ResTable& res = theme->getResTable(); + AssetManager2* assetmanager = theme->GetAssetManager(); ResTable_config config; Res_value value; int indices_idx = 0; // Load default style from attribute, if specified... - uint32_t def_style_bag_type_set_flags = 0; + uint32_t def_style_flags = 0u; if (def_style_attr != 0) { Res_value value; - if (theme->getAttribute(def_style_attr, &value, - &def_style_bag_type_set_flags) >= 0) { + if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) { if (value.dataType == Res_value::TYPE_REFERENCE) { - def_style_res = value.data; + def_style_resid = value.data; } } } - // Retrieve the style class associated with the current XML tag. - int style = 0; - uint32_t style_bag_type_set_flags = 0; + // Retrieve the style resource ID associated with the current XML tag's style attribute. + uint32_t style_resid = 0u; + uint32_t style_flags = 0u; if (xml_parser != nullptr) { ssize_t idx = xml_parser->indexOfStyle(); if (idx >= 0 && xml_parser->getAttributeValue(idx, &value) >= 0) { if (value.dataType == value.TYPE_ATTRIBUTE) { - if (theme->getAttribute(value.data, &value, &style_bag_type_set_flags) < 0) { + // Resolve the attribute with out theme. + if (theme->GetAttribute(value.data, &value, &style_flags) == kInvalidCookie) { value.dataType = Res_value::TYPE_NULL; } } + if (value.dataType == value.TYPE_REFERENCE) { - style = value.data; + style_resid = value.data; } } } - // Now lock down the resource object and start pulling stuff from it. - res.lock(); - // Retrieve the default style bag, if requested. - const ResTable::bag_entry* def_style_attr_start = nullptr; - uint32_t def_style_type_set_flags = 0; - ssize_t bag_off = def_style_res != 0 - ? res.getBagLocked(def_style_res, &def_style_attr_start, - &def_style_type_set_flags) - : -1; - def_style_type_set_flags |= def_style_bag_type_set_flags; - const ResTable::bag_entry* const def_style_attr_end = - def_style_attr_start + (bag_off >= 0 ? bag_off : 0); - BagAttributeFinder def_style_attr_finder(def_style_attr_start, - def_style_attr_end); + const ResolvedBag* default_style_bag = nullptr; + if (def_style_resid != 0) { + default_style_bag = assetmanager->GetBag(def_style_resid); + if (default_style_bag != nullptr) { + def_style_flags |= default_style_bag->type_spec_flags; + } + } + + BagAttributeFinder def_style_attr_finder(default_style_bag); // Retrieve the style class bag, if requested. - const ResTable::bag_entry* style_attr_start = nullptr; - uint32_t style_type_set_flags = 0; - bag_off = - style != 0 - ? res.getBagLocked(style, &style_attr_start, &style_type_set_flags) - : -1; - style_type_set_flags |= style_bag_type_set_flags; - const ResTable::bag_entry* const style_attr_end = - style_attr_start + (bag_off >= 0 ? bag_off : 0); - BagAttributeFinder style_attr_finder(style_attr_start, style_attr_end); + const ResolvedBag* xml_style_bag = nullptr; + if (style_resid != 0) { + xml_style_bag = assetmanager->GetBag(style_resid); + if (xml_style_bag != nullptr) { + style_flags |= xml_style_bag->type_spec_flags; + } + } + + BagAttributeFinder xml_style_attr_finder(xml_style_bag); // Retrieve the XML attributes, if requested. - static const ssize_t kXmlBlock = 0x10000000; XmlAttributeFinder xml_attr_finder(xml_parser); - const size_t xml_attr_end = - xml_parser != nullptr ? xml_parser->getAttributeCount() : 0; // Now iterate through all of the attributes that the client has requested, // filling in each with whatever data we can find. @@ -289,8 +280,8 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident); } - ssize_t block = kXmlBlock; - uint32_t type_set_flags = 0; + ApkAssetsCookie cookie = kInvalidCookie; + uint32_t type_set_flags = 0u; value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; @@ -302,7 +293,7 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s // Walk through the xml attributes looking for the requested attribute. const size_t xml_attr_idx = xml_attr_finder.Find(cur_ident); - if (xml_attr_idx != xml_attr_end) { + if (xml_attr_idx != xml_attr_finder.end()) { // We found the attribute we were looking for. xml_parser->getAttributeValue(xml_attr_idx, &value); if (kDebugStyles) { @@ -312,12 +303,12 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s if (value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) { // Walk through the style class values looking for the requested attribute. - const ResTable::bag_entry* const style_attr_entry = style_attr_finder.Find(cur_ident); - if (style_attr_entry != style_attr_end) { + const ResolvedBag::Entry* entry = xml_style_attr_finder.Find(cur_ident); + if (entry != xml_style_attr_finder.end()) { // We found the attribute we were looking for. - block = style_attr_entry->stringBlock; - type_set_flags = style_type_set_flags; - value = style_attr_entry->map.value; + cookie = entry->cookie; + type_set_flags = style_flags; + value = entry->value; if (kDebugStyles) { ALOGI("-> From style: type=0x%x, data=0x%08x", value.dataType, value.data); } @@ -326,25 +317,25 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s if (value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) { // Walk through the default style values looking for the requested attribute. - const ResTable::bag_entry* const def_style_attr_entry = def_style_attr_finder.Find(cur_ident); - if (def_style_attr_entry != def_style_attr_end) { + const ResolvedBag::Entry* entry = def_style_attr_finder.Find(cur_ident); + if (entry != def_style_attr_finder.end()) { // We found the attribute we were looking for. - block = def_style_attr_entry->stringBlock; - type_set_flags = style_type_set_flags; - value = def_style_attr_entry->map.value; + cookie = entry->cookie; + type_set_flags = def_style_flags; + value = entry->value; if (kDebugStyles) { ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data); } } } - uint32_t resid = 0; + uint32_t resid = 0u; if (value.dataType != Res_value::TYPE_NULL) { // Take care of resolving the found resource to its final value. - ssize_t new_block = - theme->resolveAttributeReference(&value, block, &resid, &type_set_flags, &config); - if (new_block >= 0) { - block = new_block; + ApkAssetsCookie new_cookie = + theme->ResolveAttributeReference(cookie, &value, &config, &type_set_flags, &resid); + if (new_cookie != kInvalidCookie) { + cookie = new_cookie; } if (kDebugStyles) { @@ -352,14 +343,15 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s } } else if (value.data != Res_value::DATA_NULL_EMPTY) { // If we still don't have a value for this attribute, try to find it in the theme! - ssize_t new_block = theme->getAttribute(cur_ident, &value, &type_set_flags); - if (new_block >= 0) { + ApkAssetsCookie new_cookie = theme->GetAttribute(cur_ident, &value, &type_set_flags); + if (new_cookie != kInvalidCookie) { if (kDebugStyles) { ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data); } - new_block = res.resolveReference(&value, new_block, &resid, &type_set_flags, &config); - if (new_block >= 0) { - block = new_block; + new_cookie = + assetmanager->ResolveReference(new_cookie, &value, &config, &type_set_flags, &resid); + if (new_cookie != kInvalidCookie) { + cookie = new_cookie; } if (kDebugStyles) { @@ -375,7 +367,7 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s } value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; - block = kXmlBlock; + cookie = kInvalidCookie; } if (kDebugStyles) { @@ -385,9 +377,7 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s // Write the final value back to Java. out_values[STYLE_TYPE] = value.dataType; out_values[STYLE_DATA] = value.data; - out_values[STYLE_ASSET_COOKIE] = - block != kXmlBlock ? static_cast(res.getTableCookie(block)) - : static_cast(-1); + out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); out_values[STYLE_RESOURCE_ID] = resid; out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; out_values[STYLE_DENSITY] = config.density; @@ -402,36 +392,28 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s out_values += STYLE_NUM_ENTRIES; } - res.unlock(); - // out_indices must NOT be nullptr. out_indices[0] = indices_idx; } -bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser, - uint32_t* attrs, size_t attrs_length, - uint32_t* out_values, uint32_t* out_indices) { +bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, uint32_t* attrs, + size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) { ResTable_config config; Res_value value; int indices_idx = 0; - // Now lock down the resource object and start pulling stuff from it. - res->lock(); - // Retrieve the XML attributes, if requested. const size_t xml_attr_count = xml_parser->getAttributeCount(); size_t ix = 0; uint32_t cur_xml_attr = xml_parser->getAttributeNameResID(ix); - static const ssize_t kXmlBlock = 0x10000000; - // Now iterate through all of the attributes that the client has requested, // filling in each with whatever data we can find. for (size_t ii = 0; ii < attrs_length; ii++) { const uint32_t cur_ident = attrs[ii]; - ssize_t block = kXmlBlock; - uint32_t type_set_flags = 0; + ApkAssetsCookie cookie = kInvalidCookie; + uint32_t type_set_flags = 0u; value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; @@ -450,28 +432,27 @@ bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser, cur_xml_attr = xml_parser->getAttributeNameResID(ix); } - uint32_t resid = 0; + uint32_t resid = 0u; if (value.dataType != Res_value::TYPE_NULL) { // Take care of resolving the found resource to its final value. - // printf("Resolving attribute reference\n"); - ssize_t new_block = res->resolveReference(&value, block, &resid, - &type_set_flags, &config); - if (new_block >= 0) block = new_block; + ApkAssetsCookie new_cookie = + assetmanager->ResolveReference(cookie, &value, &config, &type_set_flags, &resid); + if (new_cookie != kInvalidCookie) { + cookie = new_cookie; + } } // Deal with the special @null value -- it turns back to TYPE_NULL. if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; - block = kXmlBlock; + cookie = kInvalidCookie; } // Write the final value back to Java. out_values[STYLE_TYPE] = value.dataType; out_values[STYLE_DATA] = value.data; - out_values[STYLE_ASSET_COOKIE] = - block != kXmlBlock ? static_cast(res->getTableCookie(block)) - : static_cast(-1); + out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); out_values[STYLE_RESOURCE_ID] = resid; out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; out_values[STYLE_DENSITY] = config.density; @@ -485,8 +466,6 @@ bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser, out_values += STYLE_NUM_ENTRIES; } - res->unlock(); - if (out_indices != nullptr) { out_indices[0] = indices_idx; } diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp index 28548e27baf..e08848f891f 100644 --- a/libs/androidfw/LoadedArsc.cpp +++ b/libs/androidfw/LoadedArsc.cpp @@ -324,8 +324,6 @@ bool LoadedPackage::FindEntry(const TypeSpecPtr& type_spec_ptr, uint16_t entry_i bool LoadedPackage::FindEntry(uint8_t type_idx, uint16_t entry_idx, const ResTable_config& config, FindEntryResult* out_entry) const { - ATRACE_CALL(); - // If the type IDs are offset in this package, we need to take that into account when searching // for a type. const TypeSpecPtr& ptr = type_specs_[type_idx - type_id_offset_]; diff --git a/libs/androidfw/include/androidfw/AttributeFinder.h b/libs/androidfw/include/androidfw/AttributeFinder.h index f281921824e..03fad4947df 100644 --- a/libs/androidfw/include/androidfw/AttributeFinder.h +++ b/libs/androidfw/include/androidfw/AttributeFinder.h @@ -58,6 +58,7 @@ class BackTrackingAttributeFinder { BackTrackingAttributeFinder(const Iterator& begin, const Iterator& end); Iterator Find(uint32_t attr); + inline Iterator end(); private: void JumpToClosestAttribute(uint32_t package_id); @@ -201,6 +202,11 @@ Iterator BackTrackingAttributeFinder::Find(uint32_t attr) { return end_; } +template +Iterator BackTrackingAttributeFinder::end() { + return end_; +} + } // namespace android #endif // ANDROIDFW_ATTRIBUTE_FINDER_H diff --git a/libs/androidfw/include/androidfw/AttributeResolution.h b/libs/androidfw/include/androidfw/AttributeResolution.h index 69b76041484..35ef98d8c70 100644 --- a/libs/androidfw/include/androidfw/AttributeResolution.h +++ b/libs/androidfw/include/androidfw/AttributeResolution.h @@ -17,7 +17,8 @@ #ifndef ANDROIDFW_ATTRIBUTERESOLUTION_H #define ANDROIDFW_ATTRIBUTERESOLUTION_H -#include +#include "androidfw/AssetManager2.h" +#include "androidfw/ResourceTypes.h" namespace android { @@ -42,19 +43,19 @@ enum { // `out_values` must NOT be nullptr. // `out_indices` may be nullptr. -bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, +bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_resid, uint32_t* src_values, size_t src_values_length, uint32_t* attrs, size_t attrs_length, uint32_t* out_values, uint32_t* out_indices); // `out_values` must NOT be nullptr. // `out_indices` is NOT optional and must NOT be nullptr. -void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, - uint32_t def_style_res, const uint32_t* attrs, size_t attrs_length, +void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, + uint32_t def_style_resid, const uint32_t* attrs, size_t attrs_length, uint32_t* out_values, uint32_t* out_indices); // `out_values` must NOT be nullptr. // `out_indices` may be nullptr. -bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser, uint32_t* attrs, +bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, uint32_t* attrs, size_t attrs_length, uint32_t* out_values, uint32_t* out_indices); } // namespace android diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h index 965e2dbd2fb..1775f5070f4 100644 --- a/libs/androidfw/include/androidfw/LoadedArsc.h +++ b/libs/androidfw/include/androidfw/LoadedArsc.h @@ -45,16 +45,17 @@ struct FindEntryResult { // A pointer to the resource table entry for this resource. // If the size of the entry is > sizeof(ResTable_entry), it can be cast to // a ResTable_map_entry and processed as a bag/map. - const ResTable_entry* entry = nullptr; + const ResTable_entry* entry; - // The configuration for which the resulting entry was defined. - const ResTable_config* config = nullptr; + // The configuration for which the resulting entry was defined. This points to a structure that + // is already swapped to host endianness. + const ResTable_config* config; - // Stores the resulting bitmask of configuration axis with which the resource value varies. - uint32_t type_flags = 0u; + // The bitmask of configuration axis with which the resource value varies. + uint32_t type_flags; // The dynamic package ID map for the package from which this resource came from. - const DynamicRefTable* dynamic_ref_table = nullptr; + const DynamicRefTable* dynamic_ref_table; // The string pool reference to the type's name. This uses a different string pool than // the global string pool, but this is hidden from the caller. diff --git a/libs/androidfw/include/androidfw/MutexGuard.h b/libs/androidfw/include/androidfw/MutexGuard.h new file mode 100644 index 00000000000..64924f43324 --- /dev/null +++ b/libs/androidfw/include/androidfw/MutexGuard.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2017 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 ANDROIDFW_MUTEXGUARD_H +#define ANDROIDFW_MUTEXGUARD_H + +#include +#include + +#include "android-base/macros.h" + +namespace android { + +template +class ScopedLock; + +// Owns the guarded object and protects access to it via a mutex. +// The guarded object is inaccessible via this class. +// The mutex is locked and the object accessed via the ScopedLock class. +// +// NOTE: The template parameter T should not be a raw pointer, since ownership +// is ambiguous and error-prone. Instead use an std::unique_ptr<>. +// +// Example use: +// +// Guarded shared_string("hello"); +// { +// ScopedLock locked_string(shared_string); +// *locked_string += " world"; +// } +// +template +class Guarded { + static_assert(!std::is_pointer::value, "T must not be a raw pointer"); + + public: + explicit Guarded() : guarded_() { + } + + template + explicit Guarded(const T& guarded, + typename std::enable_if::value>::type = void()) + : guarded_(guarded) { + } + + template + explicit Guarded(T&& guarded, + typename std::enable_if::value>::type = void()) + : guarded_(std::move(guarded)) { + } + + private: + friend class ScopedLock; + + DISALLOW_COPY_AND_ASSIGN(Guarded); + + std::mutex lock_; + T guarded_; +}; + +template +class ScopedLock { + public: + explicit ScopedLock(Guarded& guarded) : lock_(guarded.lock_), guarded_(guarded.guarded_) { + } + + T& operator*() { + return guarded_; + } + + T* operator->() { + return &guarded_; + } + + T* get() { + return &guarded_; + } + + private: + DISALLOW_COPY_AND_ASSIGN(ScopedLock); + + std::lock_guard lock_; + T& guarded_; +}; + +} // namespace android + +#endif // ANDROIDFW_MUTEXGUARD_H diff --git a/libs/androidfw/tests/AttributeResolution_test.cpp b/libs/androidfw/tests/AttributeResolution_test.cpp index 2d73ce8f8ee..cc3053798e7 100644 --- a/libs/androidfw/tests/AttributeResolution_test.cpp +++ b/libs/androidfw/tests/AttributeResolution_test.cpp @@ -21,6 +21,7 @@ #include "android-base/file.h" #include "android-base/logging.h" #include "android-base/macros.h" +#include "androidfw/AssetManager2.h" #include "TestHelpers.h" #include "data/styles/R.h" @@ -32,15 +33,14 @@ namespace android { class AttributeResolutionTest : public ::testing::Test { public: virtual void SetUp() override { - std::string contents; - ASSERT_TRUE(ReadFileFromZipToString( - GetTestDataPath() + "/styles/styles.apk", "resources.arsc", &contents)); - ASSERT_EQ(NO_ERROR, table_.add(contents.data(), contents.size(), - 1 /*cookie*/, true /*copyData*/)); + styles_assets_ = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk"); + ASSERT_NE(nullptr, styles_assets_); + assetmanager_.SetApkAssets({styles_assets_.get()}); } protected: - ResTable table_; + std::unique_ptr styles_assets_; + AssetManager2 assetmanager_; }; class AttributeResolutionXmlTest : public AttributeResolutionTest { @@ -48,13 +48,12 @@ class AttributeResolutionXmlTest : public AttributeResolutionTest { virtual void SetUp() override { AttributeResolutionTest::SetUp(); - std::string contents; - ASSERT_TRUE( - ReadFileFromZipToString(GetTestDataPath() + "/styles/styles.apk", - "res/layout/layout.xml", &contents)); + std::unique_ptr asset = + assetmanager_.OpenNonAsset("res/layout/layout.xml", Asset::ACCESS_BUFFER); + ASSERT_NE(nullptr, asset); - ASSERT_EQ(NO_ERROR, xml_parser_.setTo(contents.data(), contents.size(), - true /*copyData*/)); + ASSERT_EQ(NO_ERROR, + xml_parser_.setTo(asset->getBuffer(true), asset->getLength(), true /*copyData*/)); // Skip to the first tag. while (xml_parser_.next() != ResXMLParser::START_TAG) { @@ -66,14 +65,14 @@ class AttributeResolutionXmlTest : public AttributeResolutionTest { }; TEST_F(AttributeResolutionTest, Theme) { - ResTable::Theme theme(table_); - ASSERT_EQ(NO_ERROR, theme.applyStyle(R::style::StyleTwo)); + std::unique_ptr theme = assetmanager_.NewTheme(); + ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo)); std::array attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three, R::attr::attr_four, R::attr::attr_empty}}; std::array values; - ASSERT_TRUE(ResolveAttrs(&theme, 0 /*def_style_attr*/, 0 /*def_style_res*/, + ASSERT_TRUE(ResolveAttrs(theme.get(), 0u /*def_style_attr*/, 0u /*def_style_res*/, nullptr /*src_values*/, 0 /*src_values_length*/, attrs.data(), attrs.size(), values.data(), nullptr /*out_indices*/)); @@ -126,8 +125,8 @@ TEST_F(AttributeResolutionXmlTest, XmlParser) { R::attr::attr_four, R::attr::attr_empty}}; std::array values; - ASSERT_TRUE(RetrieveAttributes(&table_, &xml_parser_, attrs.data(), attrs.size(), values.data(), - nullptr /*out_indices*/)); + ASSERT_TRUE(RetrieveAttributes(&assetmanager_, &xml_parser_, attrs.data(), attrs.size(), + values.data(), nullptr /*out_indices*/)); uint32_t* values_cursor = values.data(); EXPECT_EQ(Res_value::TYPE_NULL, values_cursor[STYLE_TYPE]); @@ -171,15 +170,15 @@ TEST_F(AttributeResolutionXmlTest, XmlParser) { } TEST_F(AttributeResolutionXmlTest, ThemeAndXmlParser) { - ResTable::Theme theme(table_); - ASSERT_EQ(NO_ERROR, theme.applyStyle(R::style::StyleTwo)); + std::unique_ptr theme = assetmanager_.NewTheme(); + ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo)); std::array attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three, R::attr::attr_four, R::attr::attr_five, R::attr::attr_empty}}; std::array values; std::array indices; - ApplyStyle(&theme, &xml_parser_, 0 /*def_style_attr*/, 0 /*def_style_res*/, attrs.data(), + ApplyStyle(theme.get(), &xml_parser_, 0u /*def_style_attr*/, 0u /*def_style_res*/, attrs.data(), attrs.size(), values.data(), indices.data()); const uint32_t public_flag = ResTable_typeSpec::SPEC_PUBLIC; diff --git a/libs/androidfw/tests/BenchmarkHelpers.cpp b/libs/androidfw/tests/BenchmarkHelpers.cpp index 7149beef797..a8abcb5df86 100644 --- a/libs/androidfw/tests/BenchmarkHelpers.cpp +++ b/libs/androidfw/tests/BenchmarkHelpers.cpp @@ -33,12 +33,12 @@ void GetResourceBenchmarkOld(const std::vector& paths, const ResTab } } + // Make sure to force creation of the ResTable first, or else the configuration doesn't get set. + const ResTable& table = assetmanager.getResources(true); if (config != nullptr) { assetmanager.setConfiguration(*config); } - const ResTable& table = assetmanager.getResources(true); - Res_value value; ResTable_config selected_config; uint32_t flags; diff --git a/native/android/asset_manager.cpp b/native/android/asset_manager.cpp index 98e9a42d944..e70d5ea0d56 100644 --- a/native/android/asset_manager.cpp +++ b/native/android/asset_manager.cpp @@ -18,9 +18,11 @@ #include #include +#include #include #include #include +#include #include #include "jni.h" @@ -35,21 +37,20 @@ using namespace android; // ----- struct AAssetDir { - AssetDir* mAssetDir; + std::unique_ptr mAssetDir; size_t mCurFileIndex; String8 mCachedFileName; - explicit AAssetDir(AssetDir* dir) : mAssetDir(dir), mCurFileIndex(0) { } - ~AAssetDir() { delete mAssetDir; } + explicit AAssetDir(std::unique_ptr dir) : + mAssetDir(std::move(dir)), mCurFileIndex(0) { } }; // ----- struct AAsset { - Asset* mAsset; + std::unique_ptr mAsset; - explicit AAsset(Asset* asset) : mAsset(asset) { } - ~AAsset() { delete mAsset; } + explicit AAsset(std::unique_ptr asset) : mAsset(std::move(asset)) { } }; // -------------------- Public native C API -------------------- @@ -104,19 +105,18 @@ AAsset* AAssetManager_open(AAssetManager* amgr, const char* filename, int mode) return NULL; } - AssetManager* mgr = static_cast(amgr); - Asset* asset = mgr->open(filename, amMode); - if (asset == NULL) { - return NULL; + ScopedLock locked_mgr(*AssetManagerForNdkAssetManager(amgr)); + std::unique_ptr asset = locked_mgr->Open(filename, amMode); + if (asset == nullptr) { + return nullptr; } - - return new AAsset(asset); + return new AAsset(std::move(asset)); } AAssetDir* AAssetManager_openDir(AAssetManager* amgr, const char* dirName) { - AssetManager* mgr = static_cast(amgr); - return new AAssetDir(mgr->openDir(dirName)); + ScopedLock locked_mgr(*AssetManagerForNdkAssetManager(amgr)); + return new AAssetDir(locked_mgr->OpenDir(dirName)); } /** diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp index b32be736533..52d0e08e4e7 100644 --- a/rs/jni/android_renderscript_RenderScript.cpp +++ b/rs/jni/android_renderscript_RenderScript.cpp @@ -24,8 +24,9 @@ #include #include +#include #include -#include +#include #include #include @@ -1664,18 +1665,22 @@ nFileA3DCreateFromAssetStream(JNIEnv *_env, jobject _this, jlong con, jlong nati static jlong nFileA3DCreateFromAsset(JNIEnv *_env, jobject _this, jlong con, jobject _assetMgr, jstring _path) { - AssetManager* mgr = assetManagerForJavaObject(_env, _assetMgr); + Guarded* mgr = AssetManagerForJavaObject(_env, _assetMgr); if (mgr == nullptr) { return 0; } AutoJavaStringToUTF8 str(_env, _path); - Asset* asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER); - if (asset == nullptr) { - return 0; + std::unique_ptr asset; + { + ScopedLock locked_mgr(*mgr); + asset = locked_mgr->Open(str.c_str(), Asset::ACCESS_BUFFER); + if (asset == nullptr) { + return 0; + } } - jlong id = (jlong)(uintptr_t)rsaFileA3DCreateFromAsset((RsContext)con, asset); + jlong id = (jlong)(uintptr_t)rsaFileA3DCreateFromAsset((RsContext)con, asset.release()); return id; } @@ -1752,22 +1757,25 @@ static jlong nFontCreateFromAsset(JNIEnv *_env, jobject _this, jlong con, jobject _assetMgr, jstring _path, jfloat fontSize, jint dpi) { - AssetManager* mgr = assetManagerForJavaObject(_env, _assetMgr); + Guarded* mgr = AssetManagerForJavaObject(_env, _assetMgr); if (mgr == nullptr) { return 0; } AutoJavaStringToUTF8 str(_env, _path); - Asset* asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER); - if (asset == nullptr) { - return 0; + std::unique_ptr asset; + { + ScopedLock locked_mgr(*mgr); + asset = locked_mgr->Open(str.c_str(), Asset::ACCESS_BUFFER); + if (asset == nullptr) { + return 0; + } } jlong id = (jlong)(uintptr_t)rsFontCreateFromMemory((RsContext)con, str.c_str(), str.length(), fontSize, dpi, asset->getBuffer(false), asset->getLength()); - delete asset; return id; } -- GitLab From 9ad287c828a116f844e5c03346c618d83727e4ae Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Tue, 30 Jan 2018 17:11:48 -0800 Subject: [PATCH 067/671] libandroidfw: Make sure to set the 'app as lib' flag When an app is loaded as a shared library (eg. monochrome), make sure to set the bit that it loaded as such, so that conversions from package ID 7f -> shared library ID are done. Bug: 72511998 Test: make libandroidfw_tests Test: out/host//nativetest64/libandroidfw_tests/libandroidfw_tests Change-Id: Icd11b7a5adff351165ca16d5853fb5a0002c34b1 --- libs/androidfw/AssetManager2.cpp | 19 ++++++++-- .../include/androidfw/ResourceUtils.h | 2 +- libs/androidfw/tests/AssetManager2_test.cpp | 21 ++++++++++- .../tests/AttributeResolution_test.cpp | 36 +++++++++++++++++++ 4 files changed, 73 insertions(+), 5 deletions(-) diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index 2fc8e952707..20dba37fbe6 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -74,7 +74,9 @@ void AssetManager2::BuildDynamicRefTable() { if (idx == 0xff) { package_ids_[package_id] = idx = static_cast(package_groups_.size()); package_groups_.push_back({}); - package_groups_.back().dynamic_ref_table.mAssignedPackageId = package_id; + DynamicRefTable& ref_table = package_groups_.back().dynamic_ref_table; + ref_table.mAssignedPackageId = package_id; + ref_table.mAppAsLib = package->IsDynamic() && package->GetPackageId() == 0x7f; } PackageGroup* package_group = &package_groups_[idx]; @@ -105,7 +107,15 @@ void AssetManager2::BuildDynamicRefTable() { void AssetManager2::DumpToLog() const { base::ScopedLogSeverity _log(base::INFO); + LOG(INFO) << base::StringPrintf("AssetManager2(this=%p)", this); + std::string list; + for (const auto& apk_assets : apk_assets_) { + base::StringAppendF(&list, "%s,", apk_assets->GetPath().c_str()); + } + LOG(INFO) << "ApkAssets: " << list; + + list = ""; for (size_t i = 0; i < package_ids_.size(); i++) { if (package_ids_[i] != 0xff) { base::StringAppendF(&list, "%02x -> %d, ", (int) i, package_ids_[i]); @@ -116,9 +126,12 @@ void AssetManager2::DumpToLog() const { for (const auto& package_group: package_groups_) { list = ""; for (const auto& package : package_group.packages_) { - base::StringAppendF(&list, "%s(%02x), ", package->GetPackageName().c_str(), package->GetPackageId()); + base::StringAppendF(&list, "%s(%02x%s), ", package->GetPackageName().c_str(), + package->GetPackageId(), (package->IsDynamic() ? " dynamic" : "")); } - LOG(INFO) << base::StringPrintf("PG (%02x): ", package_group.dynamic_ref_table.mAssignedPackageId) << list; + LOG(INFO) << base::StringPrintf("PG (%02x): ", + package_group.dynamic_ref_table.mAssignedPackageId) + << list; } } diff --git a/libs/androidfw/include/androidfw/ResourceUtils.h b/libs/androidfw/include/androidfw/ResourceUtils.h index c2eae855bb7..d94779bf522 100644 --- a/libs/androidfw/include/androidfw/ResourceUtils.h +++ b/libs/androidfw/include/androidfw/ResourceUtils.h @@ -28,7 +28,7 @@ bool ExtractResourceName(const StringPiece& str, StringPiece* out_package, Strin StringPiece* out_entry); inline uint32_t fix_package_id(uint32_t resid, uint8_t package_id) { - return resid | (static_cast(package_id) << 24); + return (resid & 0x00ffffffu) | (static_cast(package_id) << 24); } inline uint8_t get_package_id(uint32_t resid) { diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp index 92462a6cfad..eaf79cb1290 100644 --- a/libs/androidfw/tests/AssetManager2_test.cpp +++ b/libs/androidfw/tests/AssetManager2_test.cpp @@ -59,7 +59,7 @@ class AssetManager2Test : public ::testing::Test { libclient_assets_ = ApkAssets::Load(GetTestDataPath() + "/libclient/libclient.apk"); ASSERT_NE(nullptr, libclient_assets_); - appaslib_assets_ = ApkAssets::Load(GetTestDataPath() + "/appaslib/appaslib.apk"); + appaslib_assets_ = ApkAssets::LoadAsSharedLibrary(GetTestDataPath() + "/appaslib/appaslib.apk"); ASSERT_NE(nullptr, appaslib_assets_); system_assets_ = ApkAssets::Load(GetTestDataPath() + "/system/system.apk", true /*system*/); @@ -228,6 +228,25 @@ TEST_F(AssetManager2Test, FindsBagResourceFromMultipleApkAssets) {} TEST_F(AssetManager2Test, FindsBagResourceFromSharedLibrary) { AssetManager2 assetmanager; + // libclient is built with lib_one and then lib_two in order. + // Reverse the order to test that proper package ID re-assignment is happening. + assetmanager.SetApkAssets( + {lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()}); + + const ResolvedBag* bag = assetmanager.GetBag(fix_package_id(lib_one::R::style::Theme, 0x03)); + ASSERT_NE(nullptr, bag); + ASSERT_GE(bag->entry_count, 2u); + + // First two attributes come from lib_one. + EXPECT_EQ(1, bag->entries[0].cookie); + EXPECT_EQ(0x03, get_package_id(bag->entries[0].key)); + EXPECT_EQ(1, bag->entries[1].cookie); + EXPECT_EQ(0x03, get_package_id(bag->entries[1].key)); +} + +TEST_F(AssetManager2Test, FindsStyleResourceWithParentFromSharedLibrary) { + AssetManager2 assetmanager; + // libclient is built with lib_one and then lib_two in order. // Reverse the order to test that proper package ID re-assignment is happening. assetmanager.SetApkAssets( diff --git a/libs/androidfw/tests/AttributeResolution_test.cpp b/libs/androidfw/tests/AttributeResolution_test.cpp index cc3053798e7..c8dbe205fee 100644 --- a/libs/androidfw/tests/AttributeResolution_test.cpp +++ b/libs/androidfw/tests/AttributeResolution_test.cpp @@ -22,6 +22,7 @@ #include "android-base/logging.h" #include "android-base/macros.h" #include "androidfw/AssetManager2.h" +#include "androidfw/ResourceUtils.h" #include "TestHelpers.h" #include "data/styles/R.h" @@ -64,6 +65,41 @@ class AttributeResolutionXmlTest : public AttributeResolutionTest { ResXMLTree xml_parser_; }; +TEST(AttributeResolutionLibraryTest, ApplyStyleWithDefaultStyleResId) { + AssetManager2 assetmanager; + auto apk_assets = ApkAssets::LoadAsSharedLibrary(GetTestDataPath() + "/styles/styles.apk"); + ASSERT_NE(nullptr, apk_assets); + assetmanager.SetApkAssets({apk_assets.get()}); + + std::unique_ptr theme = assetmanager.NewTheme(); + + std::array attrs{ + {fix_package_id(R::attr::attr_one, 0x02), fix_package_id(R::attr::attr_two, 0x02)}}; + std::array values; + std::array indices; + ApplyStyle(theme.get(), nullptr /*xml_parser*/, 0u /*def_style_attr*/, + fix_package_id(R::style::StyleOne, 0x02), attrs.data(), attrs.size(), values.data(), + indices.data()); + + const uint32_t public_flag = ResTable_typeSpec::SPEC_PUBLIC; + + const uint32_t* values_cursor = values.data(); + EXPECT_EQ(Res_value::TYPE_INT_DEC, values_cursor[STYLE_TYPE]); + EXPECT_EQ(1u, values_cursor[STYLE_DATA]); + EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]); + EXPECT_EQ(1u, values_cursor[STYLE_ASSET_COOKIE]); + EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]); + EXPECT_EQ(public_flag, values_cursor[STYLE_CHANGING_CONFIGURATIONS]); + + values_cursor += STYLE_NUM_ENTRIES; + EXPECT_EQ(Res_value::TYPE_INT_DEC, values_cursor[STYLE_TYPE]); + EXPECT_EQ(2u, values_cursor[STYLE_DATA]); + EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]); + EXPECT_EQ(1u, values_cursor[STYLE_ASSET_COOKIE]); + EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]); + EXPECT_EQ(public_flag, values_cursor[STYLE_CHANGING_CONFIGURATIONS]); +} + TEST_F(AttributeResolutionTest, Theme) { std::unique_ptr theme = assetmanager_.NewTheme(); ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo)); -- GitLab From 633085456e5047e16e53da6c95e193e2a0189633 Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Fri, 1 Dec 2017 18:22:37 -0800 Subject: [PATCH 068/671] Make idiomatic use of ApkAssets and AssetManager Move away from using deprecated addAssetPath methods and cache the instances of ApkAssets created. Test: CTS passes Change-Id: Ie95cd5a9e205a35806e7b142df5af02aa90d83ca --- core/java/android/app/Activity.java | 2 + core/java/android/app/ResourcesManager.java | 198 +++++++++++++++--- .../android/content/pm/PackageParser.java | 51 ++--- .../pm/split/DefaultSplitAssetLoader.java | 75 +++---- .../pm/split/SplitAssetDependencyLoader.java | 88 ++++---- core/java/android/content/res/ApkAssets.java | 37 +--- .../android/content/res/AssetManager.java | 42 +++- 7 files changed, 315 insertions(+), 178 deletions(-) diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 0bc510a13ba..aca8d4819cf 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -6362,6 +6362,8 @@ public class Activity extends ContextThemeWrapper } else { writer.print(prefix); writer.println("No AutofillManager"); } + + ResourcesManager.getInstance().dump(prefix, writer); } /** diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java index fb11272d7e6..b96e028076e 100644 --- a/core/java/android/app/ResourcesManager.java +++ b/core/java/android/app/ResourcesManager.java @@ -21,6 +21,7 @@ import static android.app.ActivityThread.DEBUG_CONFIGURATION; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.pm.ActivityInfo; +import android.content.res.ApkAssets; import android.content.res.AssetManager; import android.content.res.CompatResources; import android.content.res.CompatibilityInfo; @@ -34,6 +35,7 @@ import android.os.Trace; import android.util.ArrayMap; import android.util.DisplayMetrics; import android.util.Log; +import android.util.LruCache; import android.util.Pair; import android.util.Slog; import android.view.Display; @@ -41,9 +43,13 @@ import android.view.DisplayAdjustments; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.IndentingPrintWriter; +import java.io.IOException; +import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.Collection; import java.util.Objects; import java.util.WeakHashMap; import java.util.function.Predicate; @@ -59,12 +65,7 @@ public class ResourcesManager { * Predicate that returns true if a WeakReference is gc'ed. */ private static final Predicate> sEmptyReferencePredicate = - new Predicate>() { - @Override - public boolean test(WeakReference weakRef) { - return weakRef == null || weakRef.get() == null; - } - }; + weakRef -> weakRef == null || weakRef.get() == null; /** * The global compatibility settings. @@ -89,6 +90,48 @@ public class ResourcesManager { */ private final ArrayList> mResourceReferences = new ArrayList<>(); + private static class ApkKey { + public final String path; + public final boolean sharedLib; + public final boolean overlay; + + ApkKey(String path, boolean sharedLib, boolean overlay) { + this.path = path; + this.sharedLib = sharedLib; + this.overlay = overlay; + } + + @Override + public int hashCode() { + int result = 1; + result = 31 * result + this.path.hashCode(); + result = 31 * result + Boolean.hashCode(this.sharedLib); + result = 31 * result + Boolean.hashCode(this.overlay); + return result; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof ApkKey)) { + return false; + } + ApkKey other = (ApkKey) obj; + return this.path.equals(other.path) && this.sharedLib == other.sharedLib + && this.overlay == other.overlay; + } + } + + /** + * The ApkAssets we are caching and intend to hold strong references to. + */ + private final LruCache mLoadedApkAssets = new LruCache<>(15); + + /** + * The ApkAssets that are being referenced in the wild that we can reuse, even if they aren't + * in our LRU cache. Bonus resources :) + */ + private final ArrayMap> mCachedApkAssets = new ArrayMap<>(); + /** * Resources and base configuration override associated with an Activity. */ @@ -260,6 +303,41 @@ public class ResourcesManager { } } + private @NonNull ApkAssets loadApkAssets(String path, boolean sharedLib, boolean overlay) + throws IOException { + final ApkKey newKey = new ApkKey(path, sharedLib, overlay); + ApkAssets apkAssets = mLoadedApkAssets.get(newKey); + if (apkAssets != null) { + return apkAssets; + } + + // Optimistically check if this ApkAssets exists somewhere else. + final WeakReference apkAssetsRef = mCachedApkAssets.get(newKey); + if (apkAssetsRef != null) { + apkAssets = apkAssetsRef.get(); + if (apkAssets != null) { + mLoadedApkAssets.put(newKey, apkAssets); + return apkAssets; + } else { + // Clean up the reference. + mCachedApkAssets.remove(newKey); + } + } + + // We must load this from disk. + if (overlay) { + final String idmapPath = "/data/resource-cache/" + + path.substring(1).replace('/', '@') + + "@idmap"; + apkAssets = ApkAssets.loadOverlayFromPath(idmapPath, false /*system*/); + } else { + apkAssets = ApkAssets.loadFromPath(path, false /*system*/, sharedLib); + } + mLoadedApkAssets.put(newKey, apkAssets); + mCachedApkAssets.put(newKey, new WeakReference<>(apkAssets)); + return apkAssets; + } + /** * Creates an AssetManager from the paths within the ResourcesKey. * @@ -270,13 +348,15 @@ public class ResourcesManager { */ @VisibleForTesting protected @Nullable AssetManager createAssetManager(@NonNull final ResourcesKey key) { - AssetManager assets = new AssetManager(); + final ArrayList apkAssets = new ArrayList<>(); // resDir can be null if the 'android' package is creating a new Resources object. // This is fine, since each AssetManager automatically loads the 'android' package // already. if (key.mResDir != null) { - if (assets.addAssetPath(key.mResDir) == 0) { + try { + apkAssets.add(loadApkAssets(key.mResDir, false /*sharedLib*/, false /*overlay*/)); + } catch (IOException e) { Log.e(TAG, "failed to add asset path " + key.mResDir); return null; } @@ -284,7 +364,10 @@ public class ResourcesManager { if (key.mSplitResDirs != null) { for (final String splitResDir : key.mSplitResDirs) { - if (assets.addAssetPath(splitResDir) == 0) { + try { + apkAssets.add(loadApkAssets(splitResDir, false /*sharedLib*/, + false /*overlay*/)); + } catch (IOException e) { Log.e(TAG, "failed to add split asset path " + splitResDir); return null; } @@ -293,7 +376,13 @@ public class ResourcesManager { if (key.mOverlayDirs != null) { for (final String idmapPath : key.mOverlayDirs) { - assets.addOverlayPath(idmapPath); + try { + apkAssets.add(loadApkAssets(idmapPath, false /*sharedLib*/, true /*overlay*/)); + } catch (IOException e) { + Log.w(TAG, "failed to add overlay path " + idmapPath); + + // continue. + } } } @@ -302,16 +391,77 @@ public class ResourcesManager { if (libDir.endsWith(".apk")) { // Avoid opening files we know do not have resources, // like code-only .jar files. - if (assets.addAssetPathAsSharedLibrary(libDir) == 0) { + try { + apkAssets.add(loadApkAssets(libDir, true /*sharedLib*/, false /*overlay*/)); + } catch (IOException e) { Log.w(TAG, "Asset path '" + libDir + "' does not exist or contains no resources."); + + // continue. } } } } + + AssetManager assets = new AssetManager(); + assets.setApkAssets(apkAssets.toArray(new ApkAssets[apkAssets.size()]), + false /*invalidateCaches*/); return assets; } + private static int countLiveReferences(Collection> collection) { + int count = 0; + for (WeakReference ref : collection) { + final T value = ref != null ? ref.get() : null; + if (value != null) { + count++; + } + } + return count; + } + + /** + * @hide + */ + public void dump(String prefix, PrintWriter printWriter) { + synchronized (this) { + IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " "); + for (int i = 0; i < prefix.length() / 2; i++) { + pw.increaseIndent(); + } + + pw.println("ResourcesManager:"); + pw.increaseIndent(); + pw.print("cached apks: total="); + pw.print(mLoadedApkAssets.size()); + pw.print(" created="); + pw.print(mLoadedApkAssets.createCount()); + pw.print(" evicted="); + pw.print(mLoadedApkAssets.evictionCount()); + pw.print(" hit="); + pw.print(mLoadedApkAssets.hitCount()); + pw.print(" miss="); + pw.print(mLoadedApkAssets.missCount()); + pw.print(" max="); + pw.print(mLoadedApkAssets.maxSize()); + pw.println(); + + pw.print("total apks: "); + pw.println(countLiveReferences(mCachedApkAssets.values())); + + pw.print("resources: "); + + int references = countLiveReferences(mResourceReferences); + for (ActivityResources activityResources : mActivityResourceReferences.values()) { + references += countLiveReferences(activityResources.activityResources); + } + pw.println(references); + + pw.print("resource impls: "); + pw.println(countLiveReferences(mResourceImpls.values())); + } + } + private Configuration generateConfig(@NonNull ResourcesKey key, @NonNull DisplayMetrics dm) { Configuration config; final boolean isDefaultDisplay = (key.mDisplayId == Display.DEFAULT_DISPLAY); @@ -630,28 +780,16 @@ public class ResourcesManager { // We will create the ResourcesImpl object outside of holding this lock. } - } - - // If we're here, we didn't find a suitable ResourcesImpl to use, so create one now. - ResourcesImpl resourcesImpl = createResourcesImpl(key); - if (resourcesImpl == null) { - return null; - } - synchronized (this) { - ResourcesImpl existingResourcesImpl = findResourcesImplForKeyLocked(key); - if (existingResourcesImpl != null) { - if (DEBUG) { - Slog.d(TAG, "- got beat! existing impl=" + existingResourcesImpl - + " new impl=" + resourcesImpl); - } - resourcesImpl.getAssets().close(); - resourcesImpl = existingResourcesImpl; - } else { - // Add this ResourcesImpl to the cache. - mResourceImpls.put(key, new WeakReference<>(resourcesImpl)); + // If we're here, we didn't find a suitable ResourcesImpl to use, so create one now. + ResourcesImpl resourcesImpl = createResourcesImpl(key); + if (resourcesImpl == null) { + return null; } + // Add this ResourcesImpl to the cache. + mResourceImpls.put(key, new WeakReference<>(resourcesImpl)); + final Resources resources; if (activityToken != null) { resources = getOrCreateResourcesForActivityLocked(activityToken, classLoader, diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 258602ffa23..cf043e22903 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -1291,7 +1291,6 @@ public class PackageParser { */ @Deprecated public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException { - final AssetManager assets = newConfiguredAssetManager(); final PackageLite lite = parseMonolithicPackageLite(apkFile, flags); if (mOnlyCoreApps) { if (!lite.coreApp) { @@ -1300,8 +1299,9 @@ public class PackageParser { } } + final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags); try { - final Package pkg = parseBaseApk(apkFile, assets, flags); + final Package pkg = parseBaseApk(apkFile, assetLoader.getBaseAssetManager(), flags); pkg.setCodePath(apkFile.getCanonicalPath()); pkg.setUse32bitAbi(lite.use32bitAbi); return pkg; @@ -1309,26 +1309,8 @@ public class PackageParser { throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION, "Failed to get path: " + apkFile, e); } finally { - IoUtils.closeQuietly(assets); - } - } - - private static int loadApkIntoAssetManager(AssetManager assets, String apkPath, int flags) - throws PackageParserException { - if ((flags & PARSE_MUST_BE_APK) != 0 && !isApkPath(apkPath)) { - throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, - "Invalid package file: " + apkPath); - } - - // The AssetManager guarantees uniqueness for asset paths, so if this asset path - // already exists in the AssetManager, addAssetPath will only return the cookie - // assigned to it. - int cookie = assets.addAssetPath(apkPath); - if (cookie == 0) { - throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, - "Failed adding asset path: " + apkPath); + IoUtils.closeQuietly(assetLoader); } - return cookie; } private Package parseBaseApk(File apkFile, AssetManager assets, int flags) @@ -1346,13 +1328,15 @@ public class PackageParser { if (DEBUG_JAR) Slog.d(TAG, "Scanning base APK: " + apkPath); - final int cookie = loadApkIntoAssetManager(assets, apkPath, flags); - - Resources res = null; XmlResourceParser parser = null; try { - res = new Resources(assets, mMetrics, null); + final int cookie = assets.findCookieForPath(apkPath); + if (cookie == 0) { + throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, + "Failed adding asset path: " + apkPath); + } parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME); + final Resources res = new Resources(assets, mMetrics, null); final String[] outError = new String[1]; final Package pkg = parseBaseApk(apkPath, res, parser, flags, outError); @@ -1387,15 +1371,18 @@ public class PackageParser { if (DEBUG_JAR) Slog.d(TAG, "Scanning split APK: " + apkPath); - final int cookie = loadApkIntoAssetManager(assets, apkPath, flags); - final Resources res; XmlResourceParser parser = null; try { - res = new Resources(assets, mMetrics, null); - assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - Build.VERSION.RESOURCES_SDK_INT); + // This must always succeed, as the path has been added to the AssetManager before. + final int cookie = assets.findCookieForPath(apkPath); + if (cookie == 0) { + throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, + "Failed adding asset path: " + apkPath); + } + parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME); + res = new Resources(assets, mMetrics, null); final String[] outError = new String[1]; pkg = parseSplitApk(pkg, res, parser, flags, splitIndex, outError); @@ -1597,9 +1584,9 @@ public class PackageParser { int flags) throws PackageParserException { final String apkPath = fd != null ? debugPathName : apkFile.getAbsolutePath(); - ApkAssets apkAssets = null; XmlResourceParser parser = null; try { + final ApkAssets apkAssets; try { apkAssets = fd != null ? ApkAssets.loadFromFd(fd, debugPathName, false, false) @@ -1636,7 +1623,7 @@ public class PackageParser { "Failed to parse " + apkPath, e); } finally { IoUtils.closeQuietly(parser); - IoUtils.closeQuietly(apkAssets); + // TODO(b/72056911): Implement and call close() on ApkAssets. } } diff --git a/core/java/android/content/pm/split/DefaultSplitAssetLoader.java b/core/java/android/content/pm/split/DefaultSplitAssetLoader.java index 99eb4702d32..9e3a8f48996 100644 --- a/core/java/android/content/pm/split/DefaultSplitAssetLoader.java +++ b/core/java/android/content/pm/split/DefaultSplitAssetLoader.java @@ -15,10 +15,13 @@ */ package android.content.pm.split; -import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST; +import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK; import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK; import android.content.pm.PackageParser; +import android.content.pm.PackageParser.PackageParserException; +import android.content.pm.PackageParser.ParseFlags; +import android.content.res.ApkAssets; import android.content.res.AssetManager; import android.os.Build; @@ -26,6 +29,8 @@ import com.android.internal.util.ArrayUtils; import libcore.io.IoUtils; +import java.io.IOException; + /** * Loads the base and split APKs into a single AssetManager. * @hide @@ -33,68 +38,66 @@ import libcore.io.IoUtils; public class DefaultSplitAssetLoader implements SplitAssetLoader { private final String mBaseCodePath; private final String[] mSplitCodePaths; - private final int mFlags; - + private final @ParseFlags int mFlags; private AssetManager mCachedAssetManager; - public DefaultSplitAssetLoader(PackageParser.PackageLite pkg, int flags) { + public DefaultSplitAssetLoader(PackageParser.PackageLite pkg, @ParseFlags int flags) { mBaseCodePath = pkg.baseCodePath; mSplitCodePaths = pkg.splitCodePaths; mFlags = flags; } - private static void loadApkIntoAssetManager(AssetManager assets, String apkPath, int flags) - throws PackageParser.PackageParserException { - if ((flags & PackageParser.PARSE_MUST_BE_APK) != 0 && !PackageParser.isApkPath(apkPath)) { - throw new PackageParser.PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, - "Invalid package file: " + apkPath); + private static ApkAssets loadApkAssets(String path, @ParseFlags int flags) + throws PackageParserException { + if ((flags & PackageParser.PARSE_MUST_BE_APK) != 0 && !PackageParser.isApkPath(path)) { + throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, + "Invalid package file: " + path); } - if (assets.addAssetPath(apkPath) == 0) { - throw new PackageParser.PackageParserException( - INSTALL_PARSE_FAILED_BAD_MANIFEST, - "Failed adding asset path: " + apkPath); + try { + return ApkAssets.loadFromPath(path); + } catch (IOException e) { + throw new PackageParserException(INSTALL_FAILED_INVALID_APK, + "Failed to load APK at path " + path, e); } } @Override - public AssetManager getBaseAssetManager() throws PackageParser.PackageParserException { + public AssetManager getBaseAssetManager() throws PackageParserException { if (mCachedAssetManager != null) { return mCachedAssetManager; } - AssetManager assets = new AssetManager(); - try { - assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - Build.VERSION.RESOURCES_SDK_INT); - loadApkIntoAssetManager(assets, mBaseCodePath, mFlags); - - if (!ArrayUtils.isEmpty(mSplitCodePaths)) { - for (String apkPath : mSplitCodePaths) { - loadApkIntoAssetManager(assets, apkPath, mFlags); - } - } + ApkAssets[] apkAssets = new ApkAssets[(mSplitCodePaths != null + ? mSplitCodePaths.length : 0) + 1]; - mCachedAssetManager = assets; - assets = null; - return mCachedAssetManager; - } finally { - if (assets != null) { - IoUtils.closeQuietly(assets); + // Load the base. + int splitIdx = 0; + apkAssets[splitIdx++] = loadApkAssets(mBaseCodePath, mFlags); + + // Load any splits. + if (!ArrayUtils.isEmpty(mSplitCodePaths)) { + for (String apkPath : mSplitCodePaths) { + apkAssets[splitIdx++] = loadApkAssets(apkPath, mFlags); } } + + AssetManager assets = new AssetManager(); + assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + Build.VERSION.RESOURCES_SDK_INT); + assets.setApkAssets(apkAssets, false /*invalidateCaches*/); + + mCachedAssetManager = assets; + return mCachedAssetManager; } @Override - public AssetManager getSplitAssetManager(int splitIdx) - throws PackageParser.PackageParserException { + public AssetManager getSplitAssetManager(int splitIdx) throws PackageParserException { return getBaseAssetManager(); } @Override public void close() throws Exception { - if (mCachedAssetManager != null) { - IoUtils.closeQuietly(mCachedAssetManager); - } + IoUtils.closeQuietly(mCachedAssetManager); } } diff --git a/core/java/android/content/pm/split/SplitAssetDependencyLoader.java b/core/java/android/content/pm/split/SplitAssetDependencyLoader.java index 16023f0d9d9..58eaabfa62f 100644 --- a/core/java/android/content/pm/split/SplitAssetDependencyLoader.java +++ b/core/java/android/content/pm/split/SplitAssetDependencyLoader.java @@ -15,17 +15,21 @@ */ package android.content.pm.split; -import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST; import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK; import android.annotation.NonNull; +import android.content.pm.PackageManager; import android.content.pm.PackageParser; +import android.content.pm.PackageParser.PackageParserException; +import android.content.pm.PackageParser.ParseFlags; +import android.content.res.ApkAssets; import android.content.res.AssetManager; import android.os.Build; import android.util.SparseArray; import libcore.io.IoUtils; +import java.io.IOException; import java.util.ArrayList; import java.util.Collections; @@ -34,17 +38,15 @@ import java.util.Collections; * is to be used when an application opts-in to isolated split loading. * @hide */ -public class SplitAssetDependencyLoader - extends SplitDependencyLoader +public class SplitAssetDependencyLoader extends SplitDependencyLoader implements SplitAssetLoader { private final String[] mSplitPaths; - private final int mFlags; - - private String[][] mCachedPaths; - private AssetManager[] mCachedAssetManagers; + private final @ParseFlags int mFlags; + private final ApkAssets[][] mCachedSplitApks; + private final AssetManager[] mCachedAssetManagers; public SplitAssetDependencyLoader(PackageParser.PackageLite pkg, - SparseArray dependencies, int flags) { + SparseArray dependencies, @ParseFlags int flags) { super(dependencies); // The base is inserted into index 0, so we need to shift all the splits by 1. @@ -53,7 +55,7 @@ public class SplitAssetDependencyLoader System.arraycopy(pkg.splitCodePaths, 0, mSplitPaths, 1, pkg.splitCodePaths.length); mFlags = flags; - mCachedPaths = new String[mSplitPaths.length][]; + mCachedSplitApks = new ApkAssets[mSplitPaths.length][]; mCachedAssetManagers = new AssetManager[mSplitPaths.length]; } @@ -62,58 +64,60 @@ public class SplitAssetDependencyLoader return mCachedAssetManagers[splitIdx] != null; } - private static AssetManager createAssetManagerWithPaths(String[] assetPaths, int flags) - throws PackageParser.PackageParserException { - final AssetManager assets = new AssetManager(); + private static ApkAssets loadApkAssets(String path, @ParseFlags int flags) + throws PackageParserException { + if ((flags & PackageParser.PARSE_MUST_BE_APK) != 0 && !PackageParser.isApkPath(path)) { + throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, + "Invalid package file: " + path); + } + try { - assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - Build.VERSION.RESOURCES_SDK_INT); - - for (String assetPath : assetPaths) { - if ((flags & PackageParser.PARSE_MUST_BE_APK) != 0 && - !PackageParser.isApkPath(assetPath)) { - throw new PackageParser.PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, - "Invalid package file: " + assetPath); - } - - if (assets.addAssetPath(assetPath) == 0) { - throw new PackageParser.PackageParserException( - INSTALL_PARSE_FAILED_BAD_MANIFEST, - "Failed adding asset path: " + assetPath); - } - } - return assets; - } catch (Throwable e) { - IoUtils.closeQuietly(assets); - throw e; + return ApkAssets.loadFromPath(path); + } catch (IOException e) { + throw new PackageParserException(PackageManager.INSTALL_FAILED_INVALID_APK, + "Failed to load APK at path " + path, e); } } + private static AssetManager createAssetManagerWithAssets(ApkAssets[] apkAssets) { + final AssetManager assets = new AssetManager(); + assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + Build.VERSION.RESOURCES_SDK_INT); + assets.setApkAssets(apkAssets, false /*invalidateCaches*/); + return assets; + } + @Override protected void constructSplit(int splitIdx, @NonNull int[] configSplitIndices, - int parentSplitIdx) throws PackageParser.PackageParserException { - final ArrayList assetPaths = new ArrayList<>(); + int parentSplitIdx) throws PackageParserException { + final ArrayList assets = new ArrayList<>(); + + // Include parent ApkAssets. if (parentSplitIdx >= 0) { - Collections.addAll(assetPaths, mCachedPaths[parentSplitIdx]); + Collections.addAll(assets, mCachedSplitApks[parentSplitIdx]); } - assetPaths.add(mSplitPaths[splitIdx]); + // Include this ApkAssets. + assets.add(loadApkAssets(mSplitPaths[splitIdx], mFlags)); + + // Load and include all config splits for this feature. for (int configSplitIdx : configSplitIndices) { - assetPaths.add(mSplitPaths[configSplitIdx]); + assets.add(loadApkAssets(mSplitPaths[configSplitIdx], mFlags)); } - mCachedPaths[splitIdx] = assetPaths.toArray(new String[assetPaths.size()]); - mCachedAssetManagers[splitIdx] = createAssetManagerWithPaths(mCachedPaths[splitIdx], - mFlags); + + // Cache the results. + mCachedSplitApks[splitIdx] = assets.toArray(new ApkAssets[assets.size()]); + mCachedAssetManagers[splitIdx] = createAssetManagerWithAssets(mCachedSplitApks[splitIdx]); } @Override - public AssetManager getBaseAssetManager() throws PackageParser.PackageParserException { + public AssetManager getBaseAssetManager() throws PackageParserException { loadDependenciesForSplit(0); return mCachedAssetManagers[0]; } @Override - public AssetManager getSplitAssetManager(int idx) throws PackageParser.PackageParserException { + public AssetManager getSplitAssetManager(int idx) throws PackageParserException { // Since we insert the base at position 0, and PackageParser keeps splits separate from // the base, we need to adjust the index. loadDependenciesForSplit(idx + 1); diff --git a/core/java/android/content/res/ApkAssets.java b/core/java/android/content/res/ApkAssets.java index b087c48d8d4..fd664bc731d 100644 --- a/core/java/android/content/res/ApkAssets.java +++ b/core/java/android/content/res/ApkAssets.java @@ -33,8 +33,8 @@ import java.io.IOException; * making the creation of AssetManagers very cheap. * @hide */ -public final class ApkAssets implements AutoCloseable { - @GuardedBy("this") private long mNativePtr; +public final class ApkAssets { + @GuardedBy("this") private final long mNativePtr; @GuardedBy("this") private StringBlock mStringBlock; /** @@ -127,14 +127,12 @@ public final class ApkAssets implements AutoCloseable { @NonNull String getAssetPath() { synchronized (this) { - ensureValidLocked(); return nativeGetAssetPath(mNativePtr); } } CharSequence getStringFromPool(int idx) { synchronized (this) { - ensureValidLocked(); return mStringBlock.get(idx); } } @@ -151,7 +149,6 @@ public final class ApkAssets implements AutoCloseable { public @NonNull XmlResourceParser openXml(@NonNull String fileName) throws IOException { Preconditions.checkNotNull(fileName, "fileName"); synchronized (this) { - ensureValidLocked(); long nativeXmlPtr = nativeOpenXml(mNativePtr, fileName); try (XmlBlock block = new XmlBlock(null, nativeXmlPtr)) { XmlResourceParser parser = block.newParser(); @@ -170,41 +167,13 @@ public final class ApkAssets implements AutoCloseable { */ public boolean isUpToDate() { synchronized (this) { - ensureValidLocked(); return nativeIsUpToDate(mNativePtr); } } - /** - * Closes the ApkAssets and destroys the underlying native implementation. Further use of the - * ApkAssets object will cause exceptions to be thrown. - * - * Calling close on an already closed ApkAssets does nothing. - */ - @Override - public void close() { - synchronized (this) { - if (mNativePtr == 0) { - return; - } - - mStringBlock = null; - nativeDestroy(mNativePtr); - mNativePtr = 0; - } - } - @Override protected void finalize() throws Throwable { - if (mNativePtr != 0) { - nativeDestroy(mNativePtr); - } - } - - private void ensureValidLocked() { - if (mNativePtr == 0) { - throw new RuntimeException("ApkAssets is closed"); - } + nativeDestroy(mNativePtr); } private static native long nativeLoad( diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java index 2db5c6b1320..24116b42bc7 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -61,6 +61,8 @@ public final class AssetManager implements AutoCloseable { private static final Object sSync = new Object(); + private static final ApkAssets[] sEmptyApkAssets = new ApkAssets[0]; + // Not private for LayoutLib's BridgeAssetManager. @GuardedBy("sSync") static AssetManager sSystem = null; @@ -237,10 +239,16 @@ public final class AssetManager implements AutoCloseable { */ public void setApkAssets(@NonNull ApkAssets[] apkAssets, boolean invalidateCaches) { Preconditions.checkNotNull(apkAssets, "apkAssets"); + + // Copy the apkAssets, but prepend the system assets (framework + overlays). + final ApkAssets[] newApkAssets = new ApkAssets[apkAssets.length + sSystemApkAssets.length]; + System.arraycopy(sSystemApkAssets, 0, newApkAssets, 0, sSystemApkAssets.length); + System.arraycopy(apkAssets, 0, newApkAssets, sSystemApkAssets.length, apkAssets.length); + synchronized (this) { - ensureValidLocked(); - mApkAssets = apkAssets; - nativeSetApkAssets(mObject, apkAssets, invalidateCaches); + ensureOpenLocked(); + mApkAssets = newApkAssets; + nativeSetApkAssets(mObject, mApkAssets, invalidateCaches); if (invalidateCaches) { // Invalidate all caches. invalidateCachesLocked(-1); @@ -259,13 +267,37 @@ public final class AssetManager implements AutoCloseable { } /** + * Returns the set of ApkAssets loaded by this AssetManager. If the AssetManager is closed, this + * returns a 0-length array. * @hide */ public @NonNull ApkAssets[] getApkAssets() { + synchronized (this) { + if (mOpen) { + return mApkAssets; + } + } + return sEmptyApkAssets; + } + + /** + * Returns a cookie for use with the other APIs of AssetManager. + * @return 0 if the path was not found, otherwise a positive integer cookie representing + * this path in the AssetManager. + * @hide + */ + public int findCookieForPath(@NonNull String path) { + Preconditions.checkNotNull(path, "path"); synchronized (this) { ensureValidLocked(); - return mApkAssets; + final int count = mApkAssets.length; + for (int i = 0; i < count; i++) { + if (path.equals(mApkAssets[i].getAssetPath())) { + return i + 1; + } + } } + return 0; } /** @@ -345,6 +377,7 @@ public final class AssetManager implements AutoCloseable { * then this implies that ensureValidLocked() also passes. */ private void ensureOpenLocked() { + // If mOpen is true, this implies that mObject != 0. if (!mOpen) { throw new RuntimeException("AssetManager has been closed"); } @@ -1176,6 +1209,7 @@ public final class AssetManager implements AutoCloseable { if (mNumRefs == 0 && mObject != 0) { nativeDestroy(mObject); mObject = 0; + mApkAssets = sEmptyApkAssets; } } -- GitLab From 59f63bd801f10336c9f0499b7a0bc0bccb0fad2e Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Thu, 28 Dec 2017 13:01:35 -0800 Subject: [PATCH 069/671] libandroidfw: Add ApplyStyle and SetConfiguration benchmark Test: mma frameworks/base/libs/androidfw Test: adb sync system data Test: adb shell /data/benchmarktest64/libandroidfw_benchmarks/libandroidfw_benchmarks Change-Id: Ia0e868008a3b32dc8d1c69ed1f2c39f152bb7815 --- libs/androidfw/Android.bp | 1 + libs/androidfw/tests/AssetManager2_bench.cpp | 59 +++++- .../tests/AttributeResolution_bench.cpp | 175 ++++++++++++++++++ libs/androidfw/tests/BenchmarkHelpers.cpp | 12 +- libs/androidfw/tests/data/basic/R.h | 2 + libs/androidfw/tests/data/basic/basic.apk | Bin 3124 -> 5260 bytes .../tests/data/basic/res/layout/layout.xml | 25 +++ .../tests/data/basic/res/values/values.xml | 13 ++ 8 files changed, 274 insertions(+), 13 deletions(-) create mode 100644 libs/androidfw/tests/AttributeResolution_bench.cpp create mode 100644 libs/androidfw/tests/data/basic/res/layout/layout.xml diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp index 251b2e773cf..7c9078b164a 100644 --- a/libs/androidfw/Android.bp +++ b/libs/androidfw/Android.bp @@ -171,6 +171,7 @@ cc_benchmark { // Actual benchmarks. "tests/AssetManager2_bench.cpp", + "tests/AttributeResolution_bench.cpp", "tests/SparseEntry_bench.cpp", "tests/Theme_bench.cpp", ], diff --git a/libs/androidfw/tests/AssetManager2_bench.cpp b/libs/androidfw/tests/AssetManager2_bench.cpp index 85e8f25394e..437e1477296 100644 --- a/libs/androidfw/tests/AssetManager2_bench.cpp +++ b/libs/androidfw/tests/AssetManager2_bench.cpp @@ -81,17 +81,18 @@ static void BM_AssetManagerLoadFrameworkAssetsOld(benchmark::State& state) { } BENCHMARK(BM_AssetManagerLoadFrameworkAssetsOld); -static void BM_AssetManagerGetResource(benchmark::State& state) { - GetResourceBenchmark({GetTestDataPath() + "/basic/basic.apk"}, nullptr /*config*/, - basic::R::integer::number1, state); +static void BM_AssetManagerGetResource(benchmark::State& state, uint32_t resid) { + GetResourceBenchmark({GetTestDataPath() + "/basic/basic.apk"}, nullptr /*config*/, resid, state); } -BENCHMARK(BM_AssetManagerGetResource); +BENCHMARK_CAPTURE(BM_AssetManagerGetResource, number1, basic::R::integer::number1); +BENCHMARK_CAPTURE(BM_AssetManagerGetResource, deep_ref, basic::R::integer::deep_ref); -static void BM_AssetManagerGetResourceOld(benchmark::State& state) { - GetResourceBenchmarkOld({GetTestDataPath() + "/basic/basic.apk"}, nullptr /*config*/, - basic::R::integer::number1, state); +static void BM_AssetManagerGetResourceOld(benchmark::State& state, uint32_t resid) { + GetResourceBenchmarkOld({GetTestDataPath() + "/basic/basic.apk"}, nullptr /*config*/, resid, + state); } -BENCHMARK(BM_AssetManagerGetResourceOld); +BENCHMARK_CAPTURE(BM_AssetManagerGetResourceOld, number1, basic::R::integer::number1); +BENCHMARK_CAPTURE(BM_AssetManagerGetResourceOld, deep_ref, basic::R::integer::deep_ref); static void BM_AssetManagerGetLibraryResource(benchmark::State& state) { GetResourceBenchmark( @@ -196,7 +197,7 @@ BENCHMARK(BM_AssetManagerGetResourceLocales); static void BM_AssetManagerGetResourceLocalesOld(benchmark::State& state) { AssetManager assets; if (!assets.addAssetPath(String8(kFrameworkPath), nullptr /*cookie*/, false /*appAsLib*/, - false /*isSystemAssets*/)) { + true /*isSystemAssets*/)) { state.SkipWithError("Failed to load assets"); return; } @@ -211,4 +212,44 @@ static void BM_AssetManagerGetResourceLocalesOld(benchmark::State& state) { } BENCHMARK(BM_AssetManagerGetResourceLocalesOld); +static void BM_AssetManagerSetConfigurationFramework(benchmark::State& state) { + std::unique_ptr apk = ApkAssets::Load(kFrameworkPath); + if (apk == nullptr) { + state.SkipWithError("Failed to load assets"); + return; + } + + AssetManager2 assets; + assets.SetApkAssets({apk.get()}); + + ResTable_config config; + memset(&config, 0, sizeof(config)); + + while (state.KeepRunning()) { + config.sdkVersion = ~config.sdkVersion; + assets.SetConfiguration(config); + } +} +BENCHMARK(BM_AssetManagerSetConfigurationFramework); + +static void BM_AssetManagerSetConfigurationFrameworkOld(benchmark::State& state) { + AssetManager assets; + if (!assets.addAssetPath(String8(kFrameworkPath), nullptr /*cookie*/, false /*appAsLib*/, + true /*isSystemAssets*/)) { + state.SkipWithError("Failed to load assets"); + return; + } + + const ResTable& table = assets.getResources(true); + + ResTable_config config; + memset(&config, 0, sizeof(config)); + + while (state.KeepRunning()) { + config.sdkVersion = ~config.sdkVersion; + assets.setConfiguration(config); + } +} +BENCHMARK(BM_AssetManagerSetConfigurationFrameworkOld); + } // namespace android diff --git a/libs/androidfw/tests/AttributeResolution_bench.cpp b/libs/androidfw/tests/AttributeResolution_bench.cpp new file mode 100644 index 00000000000..fa300c50218 --- /dev/null +++ b/libs/androidfw/tests/AttributeResolution_bench.cpp @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2017 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 "benchmark/benchmark.h" + +//#include "android-base/stringprintf.h" +#include "androidfw/ApkAssets.h" +#include "androidfw/AssetManager.h" +#include "androidfw/AssetManager2.h" +#include "androidfw/AttributeResolution.h" +#include "androidfw/ResourceTypes.h" + +#include "BenchmarkHelpers.h" +#include "data/basic/R.h" +#include "data/styles/R.h" + +namespace app = com::android::app; +namespace basic = com::android::basic; + +namespace android { + +constexpr const static char* kFrameworkPath = "/system/framework/framework-res.apk"; +constexpr const static uint32_t Theme_Material_Light = 0x01030237u; + +static void BM_ApplyStyle(benchmark::State& state) { + std::unique_ptr styles_apk = + ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk"); + if (styles_apk == nullptr) { + state.SkipWithError("failed to load assets"); + return; + } + + AssetManager2 assetmanager; + assetmanager.SetApkAssets({styles_apk.get()}); + + std::unique_ptr asset = + assetmanager.OpenNonAsset("res/layout/layout.xml", Asset::ACCESS_BUFFER); + if (asset == nullptr) { + state.SkipWithError("failed to load layout"); + return; + } + + ResXMLTree xml_tree; + if (xml_tree.setTo(asset->getBuffer(true), asset->getLength(), false /*copyData*/) != NO_ERROR) { + state.SkipWithError("corrupt xml layout"); + return; + } + + // Skip to the first tag. + while (xml_tree.next() != ResXMLParser::START_TAG) { + } + + std::unique_ptr theme = assetmanager.NewTheme(); + theme->ApplyStyle(app::R::style::StyleTwo); + + std::array attrs{{app::R::attr::attr_one, app::R::attr::attr_two, + app::R::attr::attr_three, app::R::attr::attr_four, + app::R::attr::attr_five, app::R::attr::attr_empty}}; + std::array values; + std::array indices; + + while (state.KeepRunning()) { + ApplyStyle(theme.get(), &xml_tree, 0u /*def_style_attr*/, 0u /*def_style_res*/, attrs.data(), + attrs.size(), values.data(), indices.data()); + } +} +BENCHMARK(BM_ApplyStyle); + +static void BM_ApplyStyleFramework(benchmark::State& state) { + std::unique_ptr framework_apk = ApkAssets::Load(kFrameworkPath); + if (framework_apk == nullptr) { + state.SkipWithError("failed to load framework assets"); + return; + } + + std::unique_ptr basic_apk = + ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); + if (basic_apk == nullptr) { + state.SkipWithError("failed to load assets"); + return; + } + + AssetManager2 assetmanager; + assetmanager.SetApkAssets({framework_apk.get(), basic_apk.get()}); + + ResTable_config device_config; + memset(&device_config, 0, sizeof(device_config)); + device_config.language[0] = 'e'; + device_config.language[1] = 'n'; + device_config.country[0] = 'U'; + device_config.country[1] = 'S'; + device_config.orientation = ResTable_config::ORIENTATION_PORT; + device_config.smallestScreenWidthDp = 700; + device_config.screenWidthDp = 700; + device_config.screenHeightDp = 1024; + device_config.sdkVersion = 27; + + Res_value value; + ResTable_config config; + uint32_t flags = 0u; + ApkAssetsCookie cookie = + assetmanager.GetResource(basic::R::layout::layoutt, false /*may_be_bag*/, + 0u /*density_override*/, &value, &config, &flags); + if (cookie == kInvalidCookie) { + state.SkipWithError("failed to find R.layout.layout"); + return; + } + + size_t len = 0u; + const char* layout_path = + assetmanager.GetStringPoolForCookie(cookie)->string8At(value.data, &len); + if (layout_path == nullptr || len == 0u) { + state.SkipWithError("failed to lookup layout path"); + return; + } + + std::unique_ptr asset = assetmanager.OpenNonAsset( + StringPiece(layout_path, len).to_string(), cookie, Asset::ACCESS_BUFFER); + if (asset == nullptr) { + state.SkipWithError("failed to load layout"); + return; + } + + ResXMLTree xml_tree; + if (xml_tree.setTo(asset->getBuffer(true), asset->getLength(), false /*copyData*/) != NO_ERROR) { + state.SkipWithError("corrupt xml layout"); + return; + } + + // Skip to the first tag. + while (xml_tree.next() != ResXMLParser::START_TAG) { + } + + std::unique_ptr theme = assetmanager.NewTheme(); + theme->ApplyStyle(Theme_Material_Light); + + std::array attrs{ + {0x0101000e, 0x01010034, 0x01010095, 0x01010096, 0x01010097, 0x01010098, 0x01010099, + 0x0101009a, 0x0101009b, 0x010100ab, 0x010100af, 0x010100b0, 0x010100b1, 0x0101011f, + 0x01010120, 0x0101013f, 0x01010140, 0x0101014e, 0x0101014f, 0x01010150, 0x01010151, + 0x01010152, 0x01010153, 0x01010154, 0x01010155, 0x01010156, 0x01010157, 0x01010158, + 0x01010159, 0x0101015a, 0x0101015b, 0x0101015c, 0x0101015d, 0x0101015e, 0x0101015f, + 0x01010160, 0x01010161, 0x01010162, 0x01010163, 0x01010164, 0x01010165, 0x01010166, + 0x01010167, 0x01010168, 0x01010169, 0x0101016a, 0x0101016b, 0x0101016c, 0x0101016d, + 0x0101016e, 0x0101016f, 0x01010170, 0x01010171, 0x01010217, 0x01010218, 0x0101021d, + 0x01010220, 0x01010223, 0x01010224, 0x01010264, 0x01010265, 0x01010266, 0x010102c5, + 0x010102c6, 0x010102c7, 0x01010314, 0x01010315, 0x01010316, 0x0101035e, 0x0101035f, + 0x01010362, 0x01010374, 0x0101038c, 0x01010392, 0x01010393, 0x010103ac, 0x0101045d, + 0x010104b6, 0x010104b7, 0x010104d6, 0x010104d7, 0x010104dd, 0x010104de, 0x010104df, + 0x01010535, 0x01010536, 0x01010537, 0x01010538, 0x01010546, 0x01010567, 0x011100c9, + 0x011100ca}}; + + std::array values; + std::array indices; + while (state.KeepRunning()) { + ApplyStyle(theme.get(), &xml_tree, 0x01010084u /*def_style_attr*/, 0u /*def_style_res*/, + attrs.data(), attrs.size(), values.data(), indices.data()); + } +} +BENCHMARK(BM_ApplyStyleFramework); + +} // namespace android diff --git a/libs/androidfw/tests/BenchmarkHelpers.cpp b/libs/androidfw/tests/BenchmarkHelpers.cpp index a8abcb5df86..faddfe599af 100644 --- a/libs/androidfw/tests/BenchmarkHelpers.cpp +++ b/libs/androidfw/tests/BenchmarkHelpers.cpp @@ -42,10 +42,12 @@ void GetResourceBenchmarkOld(const std::vector& paths, const ResTab Res_value value; ResTable_config selected_config; uint32_t flags; + uint32_t last_ref = 0u; while (state.KeepRunning()) { - table.getResource(resid, &value, false /*may_be_bag*/, 0u /*density*/, &flags, - &selected_config); + ssize_t block = table.getResource(resid, &value, false /*may_be_bag*/, 0u /*density*/, &flags, + &selected_config); + table.resolveReference(&value, block, &last_ref, &flags, &selected_config); } } @@ -72,10 +74,12 @@ void GetResourceBenchmark(const std::vector& paths, const ResTable_ Res_value value; ResTable_config selected_config; uint32_t flags; + uint32_t last_id = 0u; while (state.KeepRunning()) { - assetmanager.GetResource(resid, false /* may_be_bag */, 0u /* density_override */, &value, - &selected_config, &flags); + ApkAssetsCookie cookie = assetmanager.GetResource( + resid, false /* may_be_bag */, 0u /* density_override */, &value, &selected_config, &flags); + assetmanager.ResolveReference(cookie, &value, &selected_config, &flags, &last_id); } } diff --git a/libs/androidfw/tests/data/basic/R.h b/libs/androidfw/tests/data/basic/R.h index 94a2a14ced8..b7e814fea07 100644 --- a/libs/androidfw/tests/data/basic/R.h +++ b/libs/androidfw/tests/data/basic/R.h @@ -34,6 +34,7 @@ struct R { struct layout { enum : uint32_t { main = 0x7f020000, + layoutt = 0x7f020001, }; }; @@ -55,6 +56,7 @@ struct R { number2 = 0x7f040001, ref1 = 0x7f040002, ref2 = 0x7f040003, + deep_ref = 0x7f040004, // From feature number3 = 0x80030000, diff --git a/libs/androidfw/tests/data/basic/basic.apk b/libs/androidfw/tests/data/basic/basic.apk index 18ef75e91dedc0c9fbb63bfc7b0b0a34b0782af7..1733b6a1654645caf190982b31abe9255a554e35 100644 GIT binary patch delta 2438 zcmdlY(WALRhp}Fifq|hYwOBtVu`<831WM^ust zKX?A>WWD*zb>-1t21j{&WE`X&pK%*_FlaI={NrbFc&#BZ!$Cr0U6TqQlfqLa2M*>l z8C8i{5`{B383Mf7IambmrZzAzFg#(HEWjvJFM;A%-7-TY_m)mL=-2EZ(Dt8|YiZ_e zvyTrP^^XPftE$c0Bhka2FSn<{So#J>CO6Brm9~-d4_*HdSkqwFHt*|wF{k85Ir}!$ zoZ;D>yqG(p;Q;40hN>0L4BIo3ef&;J?o?3Rsod`}Nm7z;Ykk$l#6XS6Q#wH(YL(@_ zRIN*h%h;3dF5_&-;-$nen&3)CD3ibIuq-Z+x%*i=c51bycY)r=6T^e^)ieM3=B-*z?4J}Ommdb-0y$L zLFD-TZ0_8J6P#pZN(xoljvYC9G$H9x!lMI^mWkiG;Py-TDTfi~;?2$Vo&RL}{wZ3$ z{=UF>;*s*7Pu|&me;51iWy;09ZbwQS_=GJ(Sgu_Uab=fjQZ8U(d%$(4Nuplopm7D` z+ig5@PZdrxP3%q)nUJe(IHlXuVV2m>Clnr$^A5161MtAo1+Pzs$|=#^a1Dhx$d`noSNO$*W$*Sms@;-RqEzFIvA%v#mM^eo|hUtNrrF5+5ZMg zRV!70_>}N!>PhcyFC_k3s6Gq-z_{-~z4?yBXQCCCXWnPr=>7Xd=|TQQjXOC_ew^1` zf9O~h|6#S-yO+~0Mwg~NJjL*J_UvoZ*BwsXd&+U9|EZT#S7*A;3$zSBd+O@8cV;Id z_6M$)S*LS;s`2($8@H`Ye``_ueE-w^Kjz#0VFHDFpOm+w8zTcl0uuv6fHyM>0|N&G z2g7C=MnT3TFW)7R3_J`BAk4?W49bf6rA5i9#d?WF#mS(!0?F_&Fo5EXkwJ#x3IhWJ zI|CFjGB8*`nIKA)fq}t+fq@~0fq|iffq|imfq`MtB^{xCi7iknId0N6IkFL3SVm1B1n6Ru*~2sL6^f zvGq(0nG7jlwIHvwFfuTJ5+PK80hH0fYz9yWF)#=)FffQSFfb@EFfeE^FfbS}Ffdp# zFfh0gq zss-7HtX6n(51S3h)X68)X2bA5ArD|Se${8nSlk&=YsGB9_WD*8WRIIgwMzdmIvp> zdIpdIY)}DSr~-B7I5B}-P2{4F2Ge0OzfzlbswhWNiWI?t-Ar=Nu%I08T0QnzW6i&X+ zEj4)>R~s)2yrO4d5S^^aEmIH4oS>FSfHxy3-GNI@hKBi!h5ZD-VW(I~L5qO(Ox6BY$C{Fg|R+y~8&Bt_vWwHggJ(Dx*oF_Z7X-wY1roqajz_@~a^D{O+M#T*v z6F{n<22EgKU|@stL6pg4Q4aaZ793IH5Q|PQFfj0fBtV#vfgxb>MhR zgNB2FK>!pCAk*2w;tY%o3~covJ_nS~%)kQXb3*t65A+xqK&F?Vn9j-{tO(Kt3UH9A z5lq-rE#wbo1*@8@DA3HvHF>FkIwSYwvjXjmJd;BO^(P4}nOq_)HF=*9|Kz#CMobeK qCtnmckwHyBpxA<8d8WyVB34WlT$5u(6!};gSQ&yD85mA;f#d;f+D$+J diff --git a/libs/androidfw/tests/data/basic/res/layout/layout.xml b/libs/androidfw/tests/data/basic/res/layout/layout.xml new file mode 100644 index 00000000000..045ede454bc --- /dev/null +++ b/libs/androidfw/tests/data/basic/res/layout/layout.xml @@ -0,0 +1,25 @@ + + +