diff options
20 files changed, 792 insertions, 100 deletions
diff --git a/api/system-current.txt b/api/system-current.txt index 16ffc3199a77..137bb19c4a60 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -41589,10 +41589,19 @@ package android.webkit { ctor public WebViewFactory(); method public static android.content.pm.PackageInfo getLoadedPackageInfo(); method public static java.lang.String getWebViewPackageName(); + method public static int loadWebViewNativeLibraryFromPackage(java.lang.String); method public static void onWebViewUpdateInstalled(); method public static void prepareWebViewInSystemServer(); method public static void prepareWebViewInZygote(); field public static final java.lang.String CHROMIUM_WEBVIEW_VMSIZE_SIZE_PROPERTY = "persist.sys.webview.vmsize"; + field public static final int LIBLOAD_ADDRESS_SPACE_NOT_RESERVED = 2; // 0x2 + field public static final int LIBLOAD_FAILED_JNI_CALL = 7; // 0x7 + field public static final int LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES = 4; // 0x4 + field public static final int LIBLOAD_FAILED_TO_LOAD_LIBRARY = 6; // 0x6 + field public static final int LIBLOAD_FAILED_TO_OPEN_RELRO_FILE = 5; // 0x5 + field public static final int LIBLOAD_FAILED_WAITING_FOR_RELRO = 3; // 0x3 + field public static final int LIBLOAD_SUCCESS = 0; // 0x0 + field public static final int LIBLOAD_WRONG_PACKAGE_NAME = 1; // 0x1 } public abstract interface WebViewFactoryProvider { diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index a20aa6687069..47133d417912 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -2651,14 +2651,12 @@ public class DevicePolicyManager { /** * @hide - * Sets the given package as the device owner. The package must already be installed and there - * shouldn't be an existing device owner registered, for this call to succeed. Also, this - * method must be called before the device is provisioned. + * Sets the given package as the device owner. + * Same as {@link #setDeviceOwner(String, String)} but without setting a device owner name. * @param packageName the package name of the application to be registered as the device owner. * @return whether the package was successfully registered as the device owner. * @throws IllegalArgumentException if the package name is null or invalid - * @throws IllegalStateException if a device owner is already registered or the device has - * already been provisioned. + * @throws IllegalStateException If the preconditions mentioned are not met. */ public boolean setDeviceOwner(String packageName) throws IllegalArgumentException, IllegalStateException { @@ -2667,15 +2665,17 @@ public class DevicePolicyManager { /** * @hide - * Sets the given package as the device owner. The package must already be installed and there - * shouldn't be an existing device owner registered, for this call to succeed. Also, this - * method must be called before the device is provisioned. + * Sets the given package as the device owner. The package must already be installed. There + * must not already be a device owner. + * Only apps with the MANAGE_PROFILE_AND_DEVICE_OWNERS permission and the shell uid can call + * this method. + * Calling this after the setup phase of the primary user has completed is allowed only if + * the caller is the shell uid, and there are no additional users and no accounts. * @param packageName the package name of the application to be registered as the device owner. * @param ownerName the human readable name of the institution that owns this device. * @return whether the package was successfully registered as the device owner. * @throws IllegalArgumentException if the package name is null or invalid - * @throws IllegalStateException if a device owner is already registered or the device has - * already been provisioned. + * @throws IllegalStateException If the preconditions mentioned are not met. */ public boolean setDeviceOwner(String packageName, String ownerName) throws IllegalArgumentException, IllegalStateException { @@ -2961,14 +2961,18 @@ public class DevicePolicyManager { /** * @hide * Sets the given component as the profile owner of the given user profile. The package must - * already be installed and there shouldn't be an existing profile owner registered for this - * user. Only the system can call this API if the user has already completed setup. + * already be installed. There must not already be a profile owner for this user. + * Only apps with the MANAGE_PROFILE_AND_DEVICE_OWNERS permission and the shell uid can call + * this method. + * Calling this after the setup phase of the specified user has completed is allowed only if: + * - the caller is SYSTEM_UID. + * - or the caller is the shell uid, and there are no accounts on the specified user. * @param admin the component name to be registered as profile owner. * @param ownerName the human readable name of the organisation associated with this DPM. * @param userHandle the userId to set the profile owner for. * @return whether the component was successfully registered as the profile owner. - * @throws IllegalArgumentException if admin is null, the package isn't installed, or - * the user has already been set up. + * @throws IllegalArgumentException if admin is null, the package isn't installed, or the + * preconditions mentioned are not met. */ public boolean setProfileOwner(ComponentName admin, String ownerName, int userHandle) throws IllegalArgumentException { diff --git a/core/java/android/provider/Browser.java b/core/java/android/provider/Browser.java index 69a05c4f0b19..bae06b80f5ca 100644 --- a/core/java/android/provider/Browser.java +++ b/core/java/android/provider/Browser.java @@ -16,6 +16,7 @@ package android.provider; +import android.annotation.RequiresPermission; import android.content.ContentResolver; import android.content.ContentUris; import android.content.ContentValues; @@ -32,6 +33,9 @@ import android.provider.BrowserContract.Searches; import android.util.Log; import android.webkit.WebIconDatabase; +import static android.Manifest.permission.READ_HISTORY_BOOKMARKS; +import static android.Manifest.permission.WRITE_HISTORY_BOOKMARKS; + public class Browser { private static final String LOGTAG = "browser"; @@ -41,6 +45,8 @@ public class Browser { * {@link android.Manifest.permission#READ_HISTORY_BOOKMARKS} permission and writing to it * requires the {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS} permission. */ + @RequiresPermission.Read(@RequiresPermission(READ_HISTORY_BOOKMARKS)) + @RequiresPermission.Write(@RequiresPermission(WRITE_HISTORY_BOOKMARKS)) public static final Uri BOOKMARKS_URI = Uri.parse("content://browser/bookmarks"); /** @@ -122,6 +128,8 @@ public class Browser { * {@link android.Manifest.permission#READ_HISTORY_BOOKMARKS} permission and writing to it * requires the {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS} permission. */ + @RequiresPermission.Read(@RequiresPermission(READ_HISTORY_BOOKMARKS)) + @RequiresPermission.Write(@RequiresPermission(WRITE_HISTORY_BOOKMARKS)) public static final Uri SEARCHES_URI = Uri.parse("content://browser/searches"); /** @@ -233,6 +241,7 @@ public class Browser { * * @param cr The ContentResolver used to access the database. */ + @RequiresPermission(READ_HISTORY_BOOKMARKS) public static final Cursor getAllBookmarks(ContentResolver cr) throws IllegalStateException { return cr.query(Bookmarks.CONTENT_URI, @@ -248,6 +257,7 @@ public class Browser { * * @param cr The ContentResolver used to access the database. */ + @RequiresPermission(READ_HISTORY_BOOKMARKS) public static final Cursor getAllVisitedUrls(ContentResolver cr) throws IllegalStateException { return cr.query(Combined.CONTENT_URI, @@ -308,6 +318,7 @@ public class Browser { * @param real If true, this is an actual visit, and should add to the * number of visits. If false, the user entered it manually. */ + @RequiresPermission(allOf = {READ_HISTORY_BOOKMARKS, WRITE_HISTORY_BOOKMARKS}) public static final void updateVisitedHistory(ContentResolver cr, String url, boolean real) { long now = System.currentTimeMillis(); @@ -358,6 +369,7 @@ public class Browser { * @param cr The ContentResolver used to access the database. * @hide pending API council approval */ + @RequiresPermission(READ_HISTORY_BOOKMARKS) public static final String[] getVisitedHistory(ContentResolver cr) { Cursor c = null; String[] str = null; @@ -393,6 +405,7 @@ public class Browser { * * @param cr The ContentResolver used to access the database. */ + @RequiresPermission(allOf = {READ_HISTORY_BOOKMARKS, WRITE_HISTORY_BOOKMARKS}) public static final void truncateHistory(ContentResolver cr) { // TODO make a single request to the provider to do this in a single transaction Cursor cursor = null; @@ -424,6 +437,7 @@ public class Browser { * @param cr The ContentResolver used to access the database. * @return boolean True if the history can be cleared. */ + @RequiresPermission(READ_HISTORY_BOOKMARKS) public static final boolean canClearHistory(ContentResolver cr) { Cursor cursor = null; boolean ret = false; @@ -446,6 +460,7 @@ public class Browser { * Requires {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS} * @param cr The ContentResolver used to access the database. */ + @RequiresPermission(WRITE_HISTORY_BOOKMARKS) public static final void clearHistory(ContentResolver cr) { deleteHistoryWhere(cr, null); } @@ -461,6 +476,7 @@ public class Browser { * @param whereClause String to limit the items affected. * null means all items. */ + @RequiresPermission(allOf = {READ_HISTORY_BOOKMARKS, WRITE_HISTORY_BOOKMARKS}) private static final void deleteHistoryWhere(ContentResolver cr, String whereClause) { Cursor cursor = null; try { @@ -486,6 +502,7 @@ public class Browser { * @param end Last date to remove. If -1, all dates after begin. * Non-inclusive. */ + @RequiresPermission(WRITE_HISTORY_BOOKMARKS) public static final void deleteHistoryTimeFrame(ContentResolver cr, long begin, long end) { String whereClause; @@ -511,6 +528,7 @@ public class Browser { * @param cr The ContentResolver used to access the database. * @param url url to remove. */ + @RequiresPermission(WRITE_HISTORY_BOOKMARKS) public static final void deleteFromHistory(ContentResolver cr, String url) { cr.delete(History.CONTENT_URI, History.URL + "=?", new String[] { url }); @@ -523,6 +541,7 @@ public class Browser { * @param cr The ContentResolver used to access the database. * @param search The string to add to the searches database. */ + @RequiresPermission(allOf = {READ_HISTORY_BOOKMARKS, WRITE_HISTORY_BOOKMARKS}) public static final void addSearchUrl(ContentResolver cr, String search) { // The content provider will take care of updating existing searches instead of duplicating ContentValues values = new ContentValues(); @@ -536,6 +555,7 @@ public class Browser { * Requires {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS} * @param cr The ContentResolver used to access the database. */ + @RequiresPermission(WRITE_HISTORY_BOOKMARKS) public static final void clearSearches(ContentResolver cr) { // FIXME: Should this clear the urls to which these searches lead? // (i.e. remove google.com/query= blah blah blah) @@ -557,6 +577,7 @@ public class Browser { * @param listener IconListener that gets the icons once they are * retrieved. */ + @RequiresPermission(READ_HISTORY_BOOKMARKS) public static final void requestAllIcons(ContentResolver cr, String where, WebIconDatabase.IconListener listener) { // Do nothing: this is no longer used. diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java index 3340c73c32db..9782d7202b73 100644 --- a/core/java/android/webkit/WebViewFactory.java +++ b/core/java/android/webkit/WebViewFactory.java @@ -76,6 +76,18 @@ public final class WebViewFactory { private static boolean sAddressSpaceReserved = false; private static PackageInfo sPackageInfo; + // Error codes for loadWebViewNativeLibraryFromPackage + public static final int LIBLOAD_SUCCESS = 0; + public static final int LIBLOAD_WRONG_PACKAGE_NAME = 1; + public static final int LIBLOAD_ADDRESS_SPACE_NOT_RESERVED = 2; + public static final int LIBLOAD_FAILED_WAITING_FOR_RELRO = 3; + public static final int LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES = 4; + + // native relro loading error codes + public static final int LIBLOAD_FAILED_TO_OPEN_RELRO_FILE = 5; + public static final int LIBLOAD_FAILED_TO_LOAD_LIBRARY = 6; + public static final int LIBLOAD_FAILED_JNI_CALL = 7; + private static class MissingWebViewPackageException extends AndroidRuntimeException { public MissingWebViewPackageException(String message) { super(message); } public MissingWebViewPackageException(Exception e) { super(e); } @@ -136,6 +148,18 @@ public final class WebViewFactory { return sPackageInfo; } + /** + * Load the native library for the given package name iff that package + * name is the same as the one providing the current webview. + */ + public static int loadWebViewNativeLibraryFromPackage(String packageName) { + sPackageInfo = findPreferredWebViewPackage(); + if (packageName != null && packageName.equals(sPackageInfo.packageName)) { + return loadNativeLibrary(); + } + return LIBLOAD_WRONG_PACKAGE_NAME; + } + static WebViewFactoryProvider getProvider() { synchronized (sProviderLock) { // For now the main purpose of this function (and the factory abstraction) is to keep @@ -434,32 +458,34 @@ public final class WebViewFactory { } } - private static void loadNativeLibrary() { + private static int loadNativeLibrary() { if (!sAddressSpaceReserved) { Log.e(LOGTAG, "can't load with relro file; address space not reserved"); - return; + return LIBLOAD_ADDRESS_SPACE_NOT_RESERVED; } try { getUpdateService().waitForRelroCreationCompleted(VMRuntime.getRuntime().is64Bit()); } catch (RemoteException e) { Log.e(LOGTAG, "error waiting for relro creation, proceeding without", e); - return; + return LIBLOAD_FAILED_WAITING_FOR_RELRO; } try { String[] args = getWebViewNativeLibraryPaths(); - boolean result = nativeLoadWithRelroFile(args[0] /* path32 */, + int result = nativeLoadWithRelroFile(args[0] /* path32 */, args[1] /* path64 */, CHROMIUM_WEBVIEW_NATIVE_RELRO_32, CHROMIUM_WEBVIEW_NATIVE_RELRO_64); - if (!result) { + if (result != LIBLOAD_SUCCESS) { Log.w(LOGTAG, "failed to load with relro file, proceeding without"); } else if (DEBUG) { Log.v(LOGTAG, "loaded with relro file"); } + return result; } catch (MissingWebViewPackageException e) { Log.e(LOGTAG, "Failed to list WebView package libraries for loadNativeLibrary", e); + return LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES; } } @@ -470,6 +496,6 @@ public final class WebViewFactory { private static native boolean nativeReserveAddressSpace(long addressSpaceToReserve); private static native boolean nativeCreateRelroFile(String lib32, String lib64, String relro32, String relro64); - private static native boolean nativeLoadWithRelroFile(String lib32, String lib64, + private static native int nativeLoadWithRelroFile(String lib32, String lib64, String relro32, String relro64); } diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index b049e494f933..652fff265980 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -1957,6 +1957,9 @@ public class Editor { if (mPositionListener != null) { mPositionListener.onScrollChanged(); } + if (mSelectionActionMode != null) { + mSelectionActionMode.invalidateContentRect(); + } } /** @@ -3085,10 +3088,12 @@ public class Editor { MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); } - menu.add(0, TextView.ID_SELECT_ALL, 0, com.android.internal.R.string.selectAll). - setAlphabeticShortcut('a'). - setShowAsAction( - MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + if (canSelectText() && !hasPasswordTransformationMethod()) { + menu.add(0, TextView.ID_SELECT_ALL, 0, com.android.internal.R.string.selectAll). + setAlphabeticShortcut('a'). + setShowAsAction( + MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + } updateReplaceItem(menu); } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 62685a1898f7..dced05198c2c 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1309,6 +1309,14 @@ <permission android:name="android.permission.MANAGE_USERS" android:protectionLevel="signature|system" /> + <!-- @hide Allows an application to set the profile owners and the device owner. + This permission is not available to third party applications.--> + <permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS" + android:permissionGroup="android.permission-group.SYSTEM_TOOLS" + android:protectionLevel="signature" + android:label="@string/permlab_manageProfileAndDeviceOwners" + android:description="@string/permdesc_manageProfileAndDeviceOwners" /> + <!-- Allows an application to get full detailed information about recently running tasks, with full fidelity to the real state. @hide --> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 6f554f08ce17..51c2062d806e 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -699,6 +699,12 @@ discover information about which applications are used on the device.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permlab_manageProfileAndDeviceOwners">Manage profile and device owners</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to set the profile/device owners. + [CHAR LIMIT=NONE] --> + <string name="permdesc_manageProfileAndDeviceOwners">Allows apps to set the profile owners and the device owner.</string> + + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_reorderTasks">reorder running apps</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permdesc_reorderTasks">Allows the app to move tasks to the diff --git a/libs/hwui/Android.common.mk b/libs/hwui/Android.common.mk index 5fca8ec9a358..836f86875dcd 100644 --- a/libs/hwui/Android.common.mk +++ b/libs/hwui/Android.common.mk @@ -24,6 +24,7 @@ LOCAL_SRC_FILES := \ thread/TaskManager.cpp \ utils/Blur.cpp \ utils/GLUtils.cpp \ + utils/LinearAllocator.cpp \ utils/SortedListImpl.cpp \ AmbientShadow.cpp \ AnimationContext.cpp \ diff --git a/libs/hwui/DeferredDisplayList.h b/libs/hwui/DeferredDisplayList.h index c92ab91d21bf..f535afb2d61a 100644 --- a/libs/hwui/DeferredDisplayList.h +++ b/libs/hwui/DeferredDisplayList.h @@ -127,7 +127,7 @@ private: } void tryRecycleState(DeferredDisplayState* state) { - mAllocator.rewindIfLastAlloc(state, sizeof(DeferredDisplayState)); + mAllocator.rewindIfLastAlloc(state); } /** diff --git a/libs/hwui/tests/Android.mk b/libs/hwui/tests/Android.mk index 51898d2a1d22..b6f0baf4bf3e 100644 --- a/libs/hwui/tests/Android.mk +++ b/libs/hwui/tests/Android.mk @@ -34,16 +34,3 @@ LOCAL_SRC_FILES += \ tests/main.cpp include $(BUILD_EXECUTABLE) - -include $(CLEAR_VARS) - -LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.common.mk -LOCAL_MODULE := hwui_unit_tests -LOCAL_MODULE_TAGS := tests - -include $(LOCAL_PATH)/Android.common.mk - -LOCAL_SRC_FILES += \ - tests/ClipAreaTests.cpp \ - -include $(BUILD_NATIVE_TEST) diff --git a/libs/hwui/unit_tests/Android.mk b/libs/hwui/unit_tests/Android.mk new file mode 100644 index 000000000000..51601b072405 --- /dev/null +++ b/libs/hwui/unit_tests/Android.mk @@ -0,0 +1,34 @@ +# +# Copyright (C) 2014 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. +# + +local_target_dir := $(TARGET_OUT_DATA)/local/tmp +LOCAL_PATH:= $(call my-dir)/.. + +include $(CLEAR_VARS) + +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.common.mk +LOCAL_MODULE := hwui_unit_tests +LOCAL_MODULE_TAGS := tests + +include $(LOCAL_PATH)/Android.common.mk + +LOCAL_SRC_FILES += \ + unit_tests/ClipAreaTests.cpp \ + unit_tests/LinearAllocatorTests.cpp \ + unit_tests/main.cpp + + +include $(BUILD_NATIVE_TEST) diff --git a/libs/hwui/tests/ClipAreaTests.cpp b/libs/hwui/unit_tests/ClipAreaTests.cpp index 166d5b6e92f6..166d5b6e92f6 100644 --- a/libs/hwui/tests/ClipAreaTests.cpp +++ b/libs/hwui/unit_tests/ClipAreaTests.cpp diff --git a/libs/hwui/unit_tests/LinearAllocatorTests.cpp b/libs/hwui/unit_tests/LinearAllocatorTests.cpp new file mode 100644 index 000000000000..b3959d169e1d --- /dev/null +++ b/libs/hwui/unit_tests/LinearAllocatorTests.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> +#include <utils/LinearAllocator.h> + +using namespace android; +using namespace android::uirenderer; + +struct SimplePair { + int one = 1; + int two = 2; +}; + +class SignalingDtor { +public: + SignalingDtor() { + mDestroyed = nullptr; + } + SignalingDtor(bool* destroyedSignal) { + mDestroyed = destroyedSignal; + *mDestroyed = false; + } + virtual ~SignalingDtor() { + if (mDestroyed) { + *mDestroyed = true; + } + } + void setSignal(bool* destroyedSignal) { + mDestroyed = destroyedSignal; + } +private: + bool* mDestroyed; +}; + +TEST(LinearAllocator, alloc) { + LinearAllocator la; + EXPECT_EQ(0u, la.usedSize()); + la.alloc(64); + // There's some internal tracking as well as padding + // so the usedSize isn't strictly defined + EXPECT_LE(64u, la.usedSize()); + EXPECT_GT(80u, la.usedSize()); + auto pair = la.alloc<SimplePair>(); + EXPECT_LE(64u + sizeof(SimplePair), la.usedSize()); + EXPECT_GT(80u + sizeof(SimplePair), la.usedSize()); + EXPECT_EQ(1, pair->one); + EXPECT_EQ(2, pair->two); +} + +TEST(LinearAllocator, dtor) { + bool destroyed[10]; + { + LinearAllocator la; + for (int i = 0; i < 5; i++) { + la.alloc<SignalingDtor>()->setSignal(destroyed + i); + la.alloc<SimplePair>(); + } + la.alloc(100); + for (int i = 0; i < 5; i++) { + auto sd = new (la) SignalingDtor(destroyed + 5 + i); + la.autoDestroy(sd); + new (la) SimplePair(); + } + la.alloc(100); + for (int i = 0; i < 10; i++) { + EXPECT_FALSE(destroyed[i]); + } + } + for (int i = 0; i < 10; i++) { + EXPECT_TRUE(destroyed[i]); + } +} + +TEST(LinearAllocator, rewind) { + bool destroyed; + { + LinearAllocator la; + auto addr = la.alloc(100); + EXPECT_LE(100u, la.usedSize()); + la.rewindIfLastAlloc(addr, 100); + EXPECT_GT(16u, la.usedSize()); + size_t emptySize = la.usedSize(); + auto sigdtor = la.alloc<SignalingDtor>(); + sigdtor->setSignal(&destroyed); + EXPECT_FALSE(destroyed); + EXPECT_LE(emptySize, la.usedSize()); + la.rewindIfLastAlloc(sigdtor); + EXPECT_TRUE(destroyed); + EXPECT_EQ(emptySize, la.usedSize()); + destroyed = false; + } + // Checking for a double-destroy case + EXPECT_EQ(destroyed, false); +} diff --git a/libs/hwui/unit_tests/how_to_run.txt b/libs/hwui/unit_tests/how_to_run.txt new file mode 100755 index 000000000000..a2d6a34726df --- /dev/null +++ b/libs/hwui/unit_tests/how_to_run.txt @@ -0,0 +1,4 @@ +mmm -j8 $ANDROID_BUILD_TOP/frameworks/base/libs/hwui/unit_tests && +adb push $ANDROID_PRODUCT_OUT/data/nativetest/hwui_unit_tests/hwui_unit_tests \ + /data/nativetest/hwui_unit_tests/hwui_unit_tests && +adb shell /data/nativetest/hwui_unit_tests/hwui_unit_tests diff --git a/libs/hwui/unit_tests/main.cpp b/libs/hwui/unit_tests/main.cpp new file mode 100644 index 000000000000..c9b96360b36b --- /dev/null +++ b/libs/hwui/unit_tests/main.cpp @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/libs/hwui/utils/LinearAllocator.cpp b/libs/hwui/utils/LinearAllocator.cpp new file mode 100644 index 000000000000..59b12cf66a89 --- /dev/null +++ b/libs/hwui/utils/LinearAllocator.cpp @@ -0,0 +1,272 @@ +/* + * Copyright 2012, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LOG_NDEBUG 1 + +#include "utils/LinearAllocator.h" + +#include <stdlib.h> +#include <utils/Log.h> + + +// The ideal size of a page allocation (these need to be multiples of 8) +#define INITIAL_PAGE_SIZE ((size_t)4096) // 4kb +#define MAX_PAGE_SIZE ((size_t)131072) // 128kb + +// The maximum amount of wasted space we can have per page +// Allocations exceeding this will have their own dedicated page +// If this is too low, we will malloc too much +// Too high, and we may waste too much space +// Must be smaller than INITIAL_PAGE_SIZE +#define MAX_WASTE_SIZE ((size_t)1024) + +#if ALIGN_DOUBLE +#define ALIGN_SZ (sizeof(double)) +#else +#define ALIGN_SZ (sizeof(int)) +#endif + +#define ALIGN(x) ((x + ALIGN_SZ - 1 ) & ~(ALIGN_SZ - 1)) +#define ALIGN_PTR(p) ((void*)(ALIGN((size_t)p))) + +#if LOG_NDEBUG +#define ADD_ALLOCATION(size) +#define RM_ALLOCATION(size) +#else +#include <utils/Thread.h> +#include <utils/Timers.h> +static size_t s_totalAllocations = 0; +static nsecs_t s_nextLog = 0; +static android::Mutex s_mutex; + +static void _logUsageLocked() { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + if (now > s_nextLog) { + s_nextLog = now + milliseconds_to_nanoseconds(10); + ALOGV("Total memory usage: %zu kb", s_totalAllocations / 1024); + } +} + +static void _addAllocation(size_t size) { + android::AutoMutex lock(s_mutex); + s_totalAllocations += size; + _logUsageLocked(); +} + +#define ADD_ALLOCATION(size) _addAllocation(size); +#define RM_ALLOCATION(size) _addAllocation(-size); +#endif + +#define min(x,y) (((x) < (y)) ? (x) : (y)) + +void* operator new(std::size_t size, android::uirenderer::LinearAllocator& la) { + return la.alloc(size); +} + +namespace android { +namespace uirenderer { + +class LinearAllocator::Page { +public: + Page* next() { return mNextPage; } + void setNext(Page* next) { mNextPage = next; } + + Page() + : mNextPage(0) + {} + + void* operator new(size_t /*size*/, void* buf) { return buf; } + + void* start() { + return (void*) (((size_t)this) + sizeof(Page)); + } + + void* end(int pageSize) { + return (void*) (((size_t)start()) + pageSize); + } + +private: + Page(const Page& /*other*/) {} + Page* mNextPage; +}; + +LinearAllocator::LinearAllocator() + : mPageSize(INITIAL_PAGE_SIZE) + , mMaxAllocSize(MAX_WASTE_SIZE) + , mNext(0) + , mCurrentPage(0) + , mPages(0) + , mTotalAllocated(0) + , mWastedSpace(0) + , mPageCount(0) + , mDedicatedPageCount(0) {} + +LinearAllocator::~LinearAllocator(void) { + while (mDtorList) { + auto node = mDtorList; + mDtorList = node->next; + node->dtor(node->addr); + } + Page* p = mPages; + while (p) { + Page* next = p->next(); + p->~Page(); + free(p); + RM_ALLOCATION(mPageSize); + p = next; + } +} + +void* LinearAllocator::start(Page* p) { + return ALIGN_PTR(((size_t*)p) + sizeof(Page)); +} + +void* LinearAllocator::end(Page* p) { + return ((char*)p) + mPageSize; +} + +bool LinearAllocator::fitsInCurrentPage(size_t size) { + return mNext && ((char*)mNext + size) <= end(mCurrentPage); +} + +void LinearAllocator::ensureNext(size_t size) { + if (fitsInCurrentPage(size)) return; + + if (mCurrentPage && mPageSize < MAX_PAGE_SIZE) { + mPageSize = min(MAX_PAGE_SIZE, mPageSize * 2); + mPageSize = ALIGN(mPageSize); + } + mWastedSpace += mPageSize; + Page* p = newPage(mPageSize); + if (mCurrentPage) { + mCurrentPage->setNext(p); + } + mCurrentPage = p; + if (!mPages) { + mPages = mCurrentPage; + } + mNext = start(mCurrentPage); +} + +void* LinearAllocator::alloc(size_t size) { + size = ALIGN(size); + if (size > mMaxAllocSize && !fitsInCurrentPage(size)) { + ALOGV("Exceeded max size %zu > %zu", size, mMaxAllocSize); + // Allocation is too large, create a dedicated page for the allocation + Page* page = newPage(size); + mDedicatedPageCount++; + page->setNext(mPages); + mPages = page; + if (!mCurrentPage) + mCurrentPage = mPages; + return start(page); + } + ensureNext(size); + void* ptr = mNext; + mNext = ((char*)mNext) + size; + mWastedSpace -= size; + return ptr; +} + +void LinearAllocator::addToDestructionList(Destructor dtor, void* addr) { + static_assert(std::is_standard_layout<DestructorNode>::value, + "DestructorNode must have standard layout"); + static_assert(std::is_trivially_destructible<DestructorNode>::value, + "DestructorNode must be trivially destructable"); + auto node = new (*this) DestructorNode(); + node->dtor = dtor; + node->addr = addr; + node->next = mDtorList; + mDtorList = node; +} + +void LinearAllocator::runDestructorFor(void* addr) { + auto node = mDtorList; + DestructorNode* previous = nullptr; + while (node) { + if (node->addr == addr) { + if (previous) { + previous->next = node->next; + } else { + mDtorList = node->next; + } + node->dtor(node->addr); + rewindIfLastAlloc(node, sizeof(DestructorNode)); + break; + } + previous = node; + node = node->next; + } +} + +void LinearAllocator::rewindIfLastAlloc(void* ptr, size_t allocSize) { + // First run the destructor as running the destructor will + // also rewind for the DestructorNode allocation which will + // have been allocated after this void* if it has a destructor + runDestructorFor(ptr); + // Don't bother rewinding across pages + allocSize = ALIGN(allocSize); + if (ptr >= start(mCurrentPage) && ptr < end(mCurrentPage) + && ptr == ((char*)mNext - allocSize)) { + mWastedSpace += allocSize; + mNext = ptr; + } +} + +LinearAllocator::Page* LinearAllocator::newPage(size_t pageSize) { + pageSize = ALIGN(pageSize + sizeof(LinearAllocator::Page)); + ADD_ALLOCATION(pageSize); + mTotalAllocated += pageSize; + mPageCount++; + void* buf = malloc(pageSize); + return new (buf) Page(); +} + +static const char* toSize(size_t value, float& result) { + if (value < 2000) { + result = value; + return "B"; + } + if (value < 2000000) { + result = value / 1024.0f; + return "KB"; + } + result = value / 1048576.0f; + return "MB"; +} + +void LinearAllocator::dumpMemoryStats(const char* prefix) { + float prettySize; + const char* prettySuffix; + prettySuffix = toSize(mTotalAllocated, prettySize); + ALOGD("%sTotal allocated: %.2f%s", prefix, prettySize, prettySuffix); + prettySuffix = toSize(mWastedSpace, prettySize); + ALOGD("%sWasted space: %.2f%s (%.1f%%)", prefix, prettySize, prettySuffix, + (float) mWastedSpace / (float) mTotalAllocated * 100.0f); + ALOGD("%sPages %zu (dedicated %zu)", prefix, mPageCount, mDedicatedPageCount); +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/utils/LinearAllocator.h b/libs/hwui/utils/LinearAllocator.h new file mode 100644 index 000000000000..d90dd825ea1d --- /dev/null +++ b/libs/hwui/utils/LinearAllocator.h @@ -0,0 +1,142 @@ +/* + * Copyright 2012, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ANDROID_LINEARALLOCATOR_H +#define ANDROID_LINEARALLOCATOR_H + +#include <stddef.h> +#include <type_traits> + +namespace android { +namespace uirenderer { + +/** + * A memory manager that internally allocates multi-kbyte buffers for placing objects in. It avoids + * the overhead of malloc when many objects are allocated. It is most useful when creating many + * small objects with a similar lifetime, and doesn't add significant overhead for large + * allocations. + */ +class LinearAllocator { +public: + LinearAllocator(); + ~LinearAllocator(); + + /** + * Reserves and returns a region of memory of at least size 'size', aligning as needed. + * Typically this is used in an object's overridden new() method or as a replacement for malloc. + * + * The lifetime of the returned buffers is tied to that of the LinearAllocator. If calling + * delete() on an object stored in a buffer is needed, it should be overridden to use + * rewindIfLastAlloc() + */ + void* alloc(size_t size); + + /** + * Allocates an instance of the template type with the default constructor + * and adds it to the automatic destruction list. + */ + template<class T> + T* alloc() { + T* ret = new (*this) T; + autoDestroy(ret); + return ret; + } + + /** + * Adds the pointer to the tracking list to have its destructor called + * when the LinearAllocator is destroyed. + */ + template<class T> + void autoDestroy(T* addr) { + if (!std::is_trivially_destructible<T>::value) { + auto dtor = [](void* addr) { ((T*)addr)->~T(); }; + addToDestructionList(dtor, addr); + } + } + + /** + * Attempt to deallocate the given buffer, with the LinearAllocator attempting to rewind its + * state if possible. + */ + void rewindIfLastAlloc(void* ptr, size_t allocSize); + + /** + * Same as rewindIfLastAlloc(void*, size_t) + */ + template<class T> + void rewindIfLastAlloc(T* ptr) { + rewindIfLastAlloc((void*)ptr, sizeof(T)); + } + + /** + * Dump memory usage statistics to the log (allocated and wasted space) + */ + void dumpMemoryStats(const char* prefix = ""); + + /** + * The number of bytes used for buffers allocated in the LinearAllocator (does not count space + * wasted) + */ + size_t usedSize() const { return mTotalAllocated - mWastedSpace; } + +private: + LinearAllocator(const LinearAllocator& other); + + class Page; + typedef void (*Destructor)(void* addr); + struct DestructorNode { + Destructor dtor; + void* addr; + DestructorNode* next = nullptr; + }; + + void addToDestructionList(Destructor, void* addr); + void runDestructorFor(void* addr); + Page* newPage(size_t pageSize); + bool fitsInCurrentPage(size_t size); + void ensureNext(size_t size); + void* start(Page *p); + void* end(Page* p); + + size_t mPageSize; + size_t mMaxAllocSize; + void* mNext; + Page* mCurrentPage; + Page* mPages; + DestructorNode* mDtorList = nullptr; + + // Memory usage tracking + size_t mTotalAllocated; + size_t mWastedSpace; + size_t mPageCount; + size_t mDedicatedPageCount; +}; + +}; // namespace uirenderer +}; // namespace android + +void* operator new(std::size_t size, android::uirenderer::LinearAllocator& la); + +#endif // ANDROID_LINEARALLOCATOR_H diff --git a/preloaded-classes b/preloaded-classes index 86bd5c9ef9ec..95d0b4276c9b 100644 --- a/preloaded-classes +++ b/preloaded-classes @@ -828,6 +828,11 @@ android.hardware.soundtrigger.SoundTriggerModule android.hardware.usb.UsbDevice android.hardware.usb.UsbDeviceConnection android.hardware.usb.UsbRequest +# Initializing android.icu.impl.ICUBinary loads the ICU data. +# Opening the files in the Zygote avoids StrictMode violations. +# It also ensures the ICU data files are mapped on boot and all +# apps will be consistent (even if files are added to /data). +android.icu.impl.ICUBinary android.inputmethodservice.ExtractEditText android.location.Location android.location.Location$1 diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 9a30f0dd8371..6b56279354ba 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -86,6 +86,8 @@ import android.provider.Settings; import android.provider.Settings.System; import android.telecom.TelecomManager; import android.text.TextUtils; +import android.util.ArrayMap; +import android.util.ArraySet; import android.util.Log; import android.util.MathUtils; import android.util.Slog; @@ -110,10 +112,8 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; -import java.util.Map; import java.util.NoSuchElementException; import java.util.Objects; -import java.util.Set; /** * The implementation of the volume manager service. @@ -407,8 +407,7 @@ public class AudioService extends IAudioService.Stub { return "0x" + Integer.toHexString(device) + ":" + deviceAddress; } - private final HashMap<String, DeviceListSpec> mConnectedDevices = - new HashMap<String, DeviceListSpec>(); + private final ArrayMap<String, DeviceListSpec> mConnectedDevices = new ArrayMap<>(); // Forced device usage for communications private int mForcedUseForComm; @@ -2830,16 +2829,22 @@ public class AudioService extends IAudioService.Stub { } } public void onServiceDisconnected(int profile) { + ArraySet<String> toRemove = null; switch (profile) { case BluetoothProfile.A2DP: synchronized (mConnectedDevices) { synchronized (mA2dpAvrcpLock) { // Disconnect ALL DEVICE_OUT_BLUETOOTH_A2DP devices - for(Map.Entry<String, DeviceListSpec> entry - : mConnectedDevices.entrySet()) { - DeviceListSpec deviceSpec = entry.getValue(); + for (int i = 0; i < mConnectedDevices.size(); i++) { + DeviceListSpec deviceSpec = mConnectedDevices.valueAt(i); if (deviceSpec.mDeviceType == AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) { - makeA2dpDeviceUnavailableNow(deviceSpec.mDeviceAddress); + toRemove = toRemove != null ? toRemove : new ArraySet<String>(); + toRemove.add(deviceSpec.mDeviceAddress); + } + } + if (toRemove != null) { + for (int i = 0; i < toRemove.size(); i++) { + makeA2dpDeviceUnavailableNow(toRemove.valueAt(i)); } } } @@ -2849,11 +2854,16 @@ public class AudioService extends IAudioService.Stub { case BluetoothProfile.A2DP_SINK: synchronized (mConnectedDevices) { // Disconnect ALL DEVICE_IN_BLUETOOTH_A2DP devices - for(Map.Entry<String, DeviceListSpec> entry - : mConnectedDevices.entrySet()) { - DeviceListSpec deviceSpec = entry.getValue(); + for(int i = 0; i < mConnectedDevices.size(); i++) { + DeviceListSpec deviceSpec = mConnectedDevices.valueAt(i); if (deviceSpec.mDeviceType == AudioSystem.DEVICE_IN_BLUETOOTH_A2DP) { - makeA2dpSrcUnavailable(deviceSpec.mDeviceAddress); + toRemove = toRemove != null ? toRemove : new ArraySet<String>(); + toRemove.add(deviceSpec.mDeviceAddress); + } + } + if (toRemove != null) { + for (int i = 0; i < toRemove.size(); i++) { + makeA2dpSrcUnavailable(toRemove.valueAt(i)); } } } @@ -4147,11 +4157,8 @@ public class AudioService extends IAudioService.Stub { // Restore device connection states synchronized (mConnectedDevices) { - Set set = mConnectedDevices.entrySet(); - Iterator i = set.iterator(); - while (i.hasNext()) { - Map.Entry device = (Map.Entry)i.next(); - DeviceListSpec spec = (DeviceListSpec)device.getValue(); + for (int i = 0; i < mConnectedDevices.size(); i++) { + DeviceListSpec spec = mConnectedDevices.valueAt(i); AudioSystem.setDeviceConnectionState( spec.mDeviceType, AudioSystem.DEVICE_STATE_AVAILABLE, @@ -4600,8 +4607,8 @@ public class AudioService extends IAudioService.Stub { int delay = 0; if ((state == 0) && ((device & mBecomingNoisyIntentDevices) != 0)) { int devices = 0; - for (String key : mConnectedDevices.keySet()) { - int dev = mConnectedDevices.get(key).mDeviceType; + for (int i = 0; i < mConnectedDevices.size(); i++) { + int dev = mConnectedDevices.valueAt(i).mDeviceType; if (((dev & AudioSystem.DEVICE_BIT_IN) == 0) && ((dev & mBecomingNoisyIntentDevices) != 0)) { devices |= dev; diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index eb9234a1029e..6fc3103257dd 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -3983,15 +3983,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { + " for device owner"); } synchronized (this) { - if (!allowedToSetDeviceOwnerOnDevice()) { - throw new IllegalStateException( - "Trying to set device owner but device is already provisioned."); - } - - if (mDeviceOwner != null && mDeviceOwner.hasDeviceOwner()) { - throw new IllegalStateException( - "Trying to set device owner but device owner is already set."); - } + enforceCanSetDeviceOwner(); // Shutting down backup manager service permanently. long ident = Binder.clearCallingIdentity(); @@ -4009,7 +4001,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // Device owner is not set and does not exist, set it. mDeviceOwner = DeviceOwner.createWithDeviceOwner(packageName, ownerName); } else { - // Device owner is not set but a profile owner exists, update Device owner state. + // Device owner state already exists, update it. mDeviceOwner.setDeviceOwner(packageName, ownerName); } mDeviceOwner.writeOwnerFile(); @@ -4225,43 +4217,23 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (!mHasFeature) { return false; } - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null); - - UserInfo info = mUserManager.getUserInfo(userHandle); - if (info == null) { - // User doesn't exist. - throw new IllegalArgumentException( - "Attempted to set profile owner for invalid userId: " + userHandle); - } - if (info.isGuest()) { - throw new IllegalStateException("Cannot set a profile owner on a guest"); - } - if (who == null || !DeviceOwner.isInstalledForUser(who.getPackageName(), userHandle)) { throw new IllegalArgumentException("Component " + who + " not installed for userId:" + userHandle); } synchronized (this) { - // Only SYSTEM_UID can override the userSetupComplete - if (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID - && hasUserSetupCompleted(userHandle)) { - throw new IllegalStateException( - "Trying to set profile owner but user is already set-up."); - } - + enforceCanSetProfileOwner(userHandle); if (mDeviceOwner == null) { // Device owner state does not exist, create it. mDeviceOwner = DeviceOwner.createWithProfileOwner(who, ownerName, userHandle); - mDeviceOwner.writeOwnerFile(); - return true; } else { - // Device owner already exists, update it. + // Device owner state already exists, update it. mDeviceOwner.setProfileOwner(who, ownerName, userHandle); - mDeviceOwner.writeOwnerFile(); - return true; } + mDeviceOwner.writeOwnerFile(); + return true; } } @@ -4451,18 +4423,77 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } /** - * Device owner can only be set on an unprovisioned device. However, if initiated via "adb", - * we also allow it if no accounts or additional users are present on the device. + * The profile owner can only be set by adb or an app with the MANAGE_PROFILE_AND_DEVICE_OWNERS + * permission. + * The profile owner can only be set before the user setup phase has completed, + * except for: + * - SYSTEM_UID + * - adb if there are not accounts. */ - private boolean allowedToSetDeviceOwnerOnDevice() { - if (!hasUserSetupCompleted(UserHandle.USER_OWNER)) { - return true; + private void enforceCanSetProfileOwner(int userHandle) { + UserInfo info = mUserManager.getUserInfo(userHandle); + if (info == null) { + // User doesn't exist. + throw new IllegalArgumentException( + "Attempted to set profile owner for invalid userId: " + userHandle); + } + if (info.isGuest()) { + throw new IllegalStateException("Cannot set a profile owner on a guest"); + } + if (getProfileOwner(userHandle) != null) { + throw new IllegalStateException("Trying to set the profile owner, but profile owner " + + "is already set."); + } + int callingUid = Binder.getCallingUid(); + if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) { + if (hasUserSetupCompleted(userHandle) && + AccountManager.get(mContext).getAccountsAsUser(userHandle).length > 0) { + throw new IllegalStateException("Not allowed to set the profile owner because " + + "there are already some accounts on the profile"); + } + return; + } + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS, null); + if (hasUserSetupCompleted(userHandle) + && UserHandle.getAppId(callingUid) != Process.SYSTEM_UID) { + throw new IllegalStateException("Cannot set the profile owner on a user which is " + + "already set-up"); } + } - int callingId = Binder.getCallingUid(); - return (callingId == Process.SHELL_UID || callingId == Process.ROOT_UID) - && mUserManager.getUserCount() == 1 - && AccountManager.get(mContext).getAccounts().length == 0; + /** + * The Device owner can only be set by adb or an app with the MANAGE_PROFILE_AND_DEVICE_OWNERS + * permission. + * The device owner can only be set before the setup phase of the primary user has completed, + * except for adb if no accounts or additional users are present on the device. + */ + private void enforceCanSetDeviceOwner() { + if (mDeviceOwner != null && mDeviceOwner.hasDeviceOwner()) { + throw new IllegalStateException("Trying to set the device owner, but device owner " + + "is already set."); + } + int callingUid = Binder.getCallingUid(); + if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) { + if (!hasUserSetupCompleted(UserHandle.USER_OWNER)) { + return; + } + if (mUserManager.getUserCount() > 1) { + throw new IllegalStateException("Not allowed to set the device owner because there " + + "are already several users on the device"); + } + if (AccountManager.get(mContext).getAccounts().length > 0) { + throw new IllegalStateException("Not allowed to set the device owner because there " + + "are already some accounts on the device"); + } + return; + } + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS, null); + if (hasUserSetupCompleted(UserHandle.USER_OWNER)) { + throw new IllegalStateException("Cannot set the device owner if the device is " + + "already set-up"); + } } private void enforceCrossUserPermission(int userHandle) { |