diff options
52 files changed, 1474 insertions, 657 deletions
diff --git a/apex/appsearch/service/Android.bp b/apex/appsearch/service/Android.bp index 73272317b5f7..04f385e8c6f6 100644 --- a/apex/appsearch/service/Android.bp +++ b/apex/appsearch/service/Android.bp @@ -20,6 +20,5 @@ java_library { "framework-appsearch", "services.core", ], - static_libs: ["icing-java-proto-lite"], apex_available: ["com.android.appsearch"], } diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java index 042f051df4d0..ce7e04c8ce04 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java @@ -17,9 +17,14 @@ package com.android.server.appsearch; import android.app.appsearch.IAppSearchManager; import android.content.Context; +import android.os.Binder; +import android.os.UserHandle; import com.android.internal.infra.AndroidFuture; +import com.android.internal.util.Preconditions; import com.android.server.SystemService; +import com.android.server.appsearch.impl.AppSearchImpl; +import com.android.server.appsearch.impl.ImplInstanceManager; import com.google.android.icing.proto.SchemaProto; @@ -40,12 +45,20 @@ public class AppSearchManagerService extends SystemService { private class Stub extends IAppSearchManager.Stub { @Override public void setSchema(byte[] schemaBytes, AndroidFuture callback) { + Preconditions.checkNotNull(schemaBytes); + Preconditions.checkNotNull(callback); + int callingUid = Binder.getCallingUidOrThrow(); + int callingUserId = UserHandle.getUserId(callingUid); + long callingIdentity = Binder.clearCallingIdentity(); try { SchemaProto schema = SchemaProto.parseFrom(schemaBytes); - throw new UnsupportedOperationException("setSchema not yet implemented: " + schema); - + AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); + impl.setSchema(callingUid, schema); + callback.complete(null); } catch (Throwable t) { callback.completeExceptionally(t); + } finally { + Binder.restoreCallingIdentity(callingIdentity); } } diff --git a/apex/appsearch/service/java/com/android/server/appsearch/impl/AppSearchImpl.java b/apex/appsearch/service/java/com/android/server/appsearch/impl/AppSearchImpl.java new file mode 100644 index 000000000000..7c97b0b8cf30 --- /dev/null +++ b/apex/appsearch/service/java/com/android/server/appsearch/impl/AppSearchImpl.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2019 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.appsearch.impl; + +import android.annotation.NonNull; +import android.annotation.UserIdInt; +import android.content.Context; + +import com.android.internal.annotations.VisibleForTesting; + +import com.google.android.icing.proto.PropertyConfigProto; +import com.google.android.icing.proto.SchemaProto; +import com.google.android.icing.proto.SchemaTypeConfigProto; + +/** + * Manages interaction with {@link FakeIcing} and other components to implement AppSearch + * functionality. + */ +public final class AppSearchImpl { + private final Context mContext; + private final @UserIdInt int mUserId; + private final FakeIcing mFakeIcing = new FakeIcing(); + + AppSearchImpl(@NonNull Context context, @UserIdInt int userId) { + mContext = context; + mUserId = userId; + } + + /** + * Updates the AppSearch schema for this app. + * + * @param callingUid The uid of the app calling AppSearch. + * @param origSchema The schema to set for this app. + */ + public void setSchema(int callingUid, @NonNull SchemaProto origSchema) { + // Rewrite schema type names to include the calling app's package and uid. + String typePrefix = getTypePrefix(callingUid); + SchemaProto.Builder schemaBuilder = origSchema.toBuilder(); + rewriteSchemaTypes(typePrefix, schemaBuilder); + + // TODO(b/145635424): Save in schema type map + // TODO(b/145635424): Apply the schema to Icing and report results + } + + /** + * Rewrites all types mentioned in the given {@code schemaBuilder} to prepend + * {@code typePrefix}. + * + * @param typePrefix The prefix to add + * @param schemaBuilder The schema to mutate + */ + @VisibleForTesting + void rewriteSchemaTypes( + @NonNull String typePrefix, @NonNull SchemaProto.Builder schemaBuilder) { + for (int typeIdx = 0; typeIdx < schemaBuilder.getTypesCount(); typeIdx++) { + SchemaTypeConfigProto.Builder typeConfigBuilder = + schemaBuilder.getTypes(typeIdx).toBuilder(); + + // Rewrite SchemaProto.types.schema_type + String newSchemaType = typePrefix + typeConfigBuilder.getSchemaType(); + typeConfigBuilder.setSchemaType(newSchemaType); + + // Rewrite SchemaProto.types.properties.schema_type + for (int propertyIdx = 0; + propertyIdx < typeConfigBuilder.getPropertiesCount(); + propertyIdx++) { + PropertyConfigProto.Builder propertyConfigBuilder = + typeConfigBuilder.getProperties(propertyIdx).toBuilder(); + if (!propertyConfigBuilder.getSchemaType().isEmpty()) { + String newPropertySchemaType = + typePrefix + propertyConfigBuilder.getSchemaType(); + propertyConfigBuilder.setSchemaType(newPropertySchemaType); + typeConfigBuilder.setProperties(propertyIdx, propertyConfigBuilder); + } + } + + schemaBuilder.setTypes(typeIdx, typeConfigBuilder); + } + } + + /** + * Returns a type prefix in a format like {@code com.example.package@1000/} or + * {@code com.example.sharedname:5678@1000/}. + */ + @NonNull + private String getTypePrefix(int callingUid) { + // For regular apps, this call will return the package name. If callingUid is an + // android:sharedUserId, this value may be another type of name and have a :uid suffix. + String callingUidName = mContext.getPackageManager().getNameForUid(callingUid); + if (callingUidName == null) { + // Not sure how this is possible --- maybe app was uninstalled? + throw new IllegalStateException("Failed to look up package name for uid " + callingUid); + } + return callingUidName + "@" + mUserId + "/"; + } +} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/impl/ImplInstanceManager.java b/apex/appsearch/service/java/com/android/server/appsearch/impl/ImplInstanceManager.java new file mode 100644 index 000000000000..395e30e89dc0 --- /dev/null +++ b/apex/appsearch/service/java/com/android/server/appsearch/impl/ImplInstanceManager.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2019 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.appsearch.impl; + +import android.annotation.NonNull; +import android.annotation.UserIdInt; +import android.content.Context; +import android.util.SparseArray; + +/** + * Manages the lifecycle of instances of {@link AppSearchImpl}. + * + * <p>These instances are managed per unique device-user. + */ +public final class ImplInstanceManager { + private static final SparseArray<AppSearchImpl> sInstances = new SparseArray<>(); + + /** + * Gets an instance of AppSearchImpl for the given user. + * + * <p>If no AppSearchImpl instance exists for this user, Icing will be initialized and one will + * be created. + * + * @param context The Android context + * @param userId The multi-user userId of the device user calling AppSearch + * @return An initialized {@link AppSearchImpl} for this user + */ + @NonNull + public static AppSearchImpl getInstance(@NonNull Context context, @UserIdInt int userId) { + AppSearchImpl instance = sInstances.get(userId); + if (instance == null) { + synchronized (ImplInstanceManager.class) { + instance = sInstances.get(userId); + if (instance == null) { + instance = new AppSearchImpl(context, userId); + sInstances.put(userId, instance); + } + } + } + return instance; + } +} diff --git a/apex/statsd/aidl/android/os/IStatsd.aidl b/apex/statsd/aidl/android/os/IStatsd.aidl index c409f516de1b..0ecf2f0b4851 100644 --- a/apex/statsd/aidl/android/os/IStatsd.aidl +++ b/apex/statsd/aidl/android/os/IStatsd.aidl @@ -212,12 +212,17 @@ interface IStatsd { * * Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS */ - oneway void unregisterPullerCallback(int atomTag, String packageName); + oneway void unregisterPullerCallback(int atomTag, String packageName); - /** - * Unregisters any pullAtomCallback for the given uid/atom. - */ - oneway void unregisterPullAtomCallback(int uid, int atomTag); + /** + * Unregisters any pullAtomCallback for the given uid/atom. + */ + oneway void unregisterPullAtomCallback(int uid, int atomTag); + + /** + * Unregisters any pullAtomCallback for the given atom. + */ + oneway void unregisterNativePullAtomCallback(int atomTag); /** * The install requires staging. diff --git a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java index 6444b4e7c9ad..81d732fb26e8 100644 --- a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java +++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java @@ -714,111 +714,6 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { } } - /** - * Helper method to extract the Parcelable controller info from a - * SynchronousResultReceiver. - */ - private static <T extends Parcelable> T awaitControllerInfo( - @Nullable SynchronousResultReceiver receiver) { - if (receiver == null) { - return null; - } - - try { - final SynchronousResultReceiver.Result result = - receiver.awaitResult(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS); - if (result.bundle != null) { - // This is the final destination for the Bundle. - result.bundle.setDefusable(true); - - final T data = result.bundle.getParcelable( - RESULT_RECEIVER_CONTROLLER_KEY); - if (data != null) { - return data; - } - } - Slog.e(TAG, "no controller energy info supplied for " + receiver.getName()); - } catch (TimeoutException e) { - Slog.w(TAG, "timeout reading " + receiver.getName() + " stats"); - } - return null; - } - - private void pullWifiActivityInfo( - int tagId, long elapsedNanos, long wallClockNanos, - List<StatsLogEventWrapper> pulledData) { - WifiManager wifiManager; - synchronized (this) { - if (mWifiManager == null) { - mWifiManager = mContext.getSystemService(WifiManager.class); - } - wifiManager = mWifiManager; - } - if (wifiManager == null) { - return; - } - long token = Binder.clearCallingIdentity(); - try { - SynchronousResultReceiver wifiReceiver = new SynchronousResultReceiver("wifi"); - wifiManager.getWifiActivityEnergyInfoAsync( - new Executor() { - @Override - public void execute(Runnable runnable) { - // run the listener on the binder thread, if it was run on the main - // thread it would deadlock since we would be waiting on ourselves - runnable.run(); - } - }, - info -> { - Bundle bundle = new Bundle(); - bundle.putParcelable(BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY, info); - wifiReceiver.send(0, bundle); - } - ); - final WifiActivityEnergyInfo wifiInfo = awaitControllerInfo(wifiReceiver); - if (wifiInfo == null) { - return; - } - StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); - e.writeLong(wifiInfo.getTimeSinceBootMillis()); - e.writeInt(wifiInfo.getStackState()); - e.writeLong(wifiInfo.getControllerTxDurationMillis()); - e.writeLong(wifiInfo.getControllerRxDurationMillis()); - e.writeLong(wifiInfo.getControllerIdleDurationMillis()); - e.writeLong(wifiInfo.getControllerEnergyUsedMicroJoules()); - pulledData.add(e); - } finally { - Binder.restoreCallingIdentity(token); - } - } - - private void pullModemActivityInfo( - int tagId, long elapsedNanos, long wallClockNanos, - List<StatsLogEventWrapper> pulledData) { - long token = Binder.clearCallingIdentity(); - synchronized (this) { - if (mTelephony == null) { - mTelephony = mContext.getSystemService(TelephonyManager.class); - } - } - if (mTelephony != null) { - SynchronousResultReceiver modemReceiver = new SynchronousResultReceiver("telephony"); - mTelephony.requestModemActivityInfo(modemReceiver); - final ModemActivityInfo modemInfo = awaitControllerInfo(modemReceiver); - StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); - e.writeLong(modemInfo.getTimestamp()); - e.writeLong(modemInfo.getSleepTimeMillis()); - e.writeLong(modemInfo.getIdleTimeMillis()); - e.writeLong(modemInfo.getTransmitPowerInfo().get(0).getTimeInMillis()); - e.writeLong(modemInfo.getTransmitPowerInfo().get(1).getTimeInMillis()); - e.writeLong(modemInfo.getTransmitPowerInfo().get(2).getTimeInMillis()); - e.writeLong(modemInfo.getTransmitPowerInfo().get(3).getTimeInMillis()); - e.writeLong(modemInfo.getTransmitPowerInfo().get(4).getTimeInMillis()); - e.writeLong(modemInfo.getReceiveTimeMillis()); - pulledData.add(e); - } - } - private void pullSystemElapsedRealtime( int tagId, long elapsedNanos, long wallClockNanos, List<StatsLogEventWrapper> pulledData) { @@ -1995,16 +1890,6 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { long wallClockNanos = SystemClock.currentTimeMicro() * 1000L; switch (tagId) { - case StatsLog.WIFI_ACTIVITY_INFO: { - pullWifiActivityInfo(tagId, elapsedNanos, wallClockNanos, ret); - break; - } - - case StatsLog.MODEM_ACTIVITY_INFO: { - pullModemActivityInfo(tagId, elapsedNanos, wallClockNanos, ret); - break; - } - case StatsLog.SYSTEM_ELAPSED_REALTIME: { pullSystemElapsedRealtime(tagId, elapsedNanos, wallClockNanos, ret); break; diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index 1ca19c3417c2..ada2f2de28f4 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -1312,6 +1312,13 @@ Status StatsService::unregisterPullAtomCallback(int32_t uid, int32_t atomTag) { return Status::ok(); } +Status StatsService::unregisterNativePullAtomCallback(int32_t atomTag) { + VLOG("StatsService::unregisterNativePullAtomCallback called."); + int32_t uid = IPCThreadState::self()->getCallingUid(); + mPullerManager->UnregisterPullAtomCallback(uid, atomTag); + return Status::ok(); +} + Status StatsService::sendBinaryPushStateChangedAtom(const android::String16& trainNameIn, const int64_t trainVersionCodeIn, const int options, diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h index c9a9072ecb92..7990e5e7d018 100644 --- a/cmds/statsd/src/StatsService.h +++ b/cmds/statsd/src/StatsService.h @@ -203,6 +203,11 @@ public: virtual Status unregisterPullAtomCallback(int32_t uid, int32_t atomTag) override; /** + * Binder call to unregister any existing callback for the given atom and calling uid. + */ + virtual Status unregisterNativePullAtomCallback(int32_t atomTag) override; + + /** * Binder call to log BinaryPushStateChanged atom. */ virtual Status sendBinaryPushStateChangedAtom( diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 0255961b75ec..9424862c56fe 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -324,8 +324,6 @@ message Atom { 228 [(allow_from_any_uid) = true]; PerfettoUploaded perfetto_uploaded = 229 [(module) = "perfetto"]; VmsClientConnectionStateChanged vms_client_connection_state_changed = 230; - GpsLocationStatusReported gps_location_status_reported = 231; - GpsTimeToFirstFixReported gps_time_to_first_fix_reported = 232; MediaProviderScanEvent media_provider_scan_event = 233 [(module) = "mediaprovider"]; MediaProviderDeletionEvent media_provider_deletion_event = 234 [(module) = "mediaprovider"]; MediaProviderPermissionEvent media_provider_permission_event = @@ -738,27 +736,6 @@ message GpsSignalQualityChanged { optional android.server.location.GpsSignalQualityEnum level = 1; } -/** - * Gps location status report - * - * Logged from: - * /frameworks/base/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java - */ -message GpsLocationStatusReported { - // Boolean stating if location was acquired - optional bool location_success = 1; -} - -/** - * Gps log time to first fix report - * - * Logged from: - * /frameworks/base/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java - */ -message GpsTimeToFirstFixReported { - // int32 reporting the time to first fix in milliseconds - optional int32 time_to_first_fix_millis = 1; -} /** * Logs when a sync manager sync state changes. diff --git a/cmds/statsd/src/external/StatsCallbackPuller.cpp b/cmds/statsd/src/external/StatsCallbackPuller.cpp index 0e6b677abb46..e5a83a27bfcf 100644 --- a/cmds/statsd/src/external/StatsCallbackPuller.cpp +++ b/cmds/statsd/src/external/StatsCallbackPuller.cpp @@ -42,7 +42,7 @@ StatsCallbackPuller::StatsCallbackPuller(int tagId, const sp<IPullAtomCallback>& } bool StatsCallbackPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) { - VLOG("StatsCallbackPuller called for tag %d", mTagId) + VLOG("StatsCallbackPuller called for tag %d", mTagId); if(mCallback == nullptr) { ALOGW("No callback registered"); return false; diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp index b5920cb6057a..b9f7a0b82f8c 100644 --- a/cmds/statsd/src/external/StatsPullerManager.cpp +++ b/cmds/statsd/src/external/StatsPullerManager.cpp @@ -68,14 +68,6 @@ std::map<PullerKey, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = { {{.atomTag = android::util::ON_DEVICE_POWER_MEASUREMENT}, {.puller = new PowerStatsPuller()}}, - // wifi_activity_energy_info - {{.atomTag = android::util::WIFI_ACTIVITY_INFO}, - {.puller = new StatsCompanionServicePuller(android::util::WIFI_ACTIVITY_INFO)}}, - - // modem_activity_info - {{.atomTag = android::util::MODEM_ACTIVITY_INFO}, - {.puller = new StatsCompanionServicePuller(android::util::MODEM_ACTIVITY_INFO)}}, - // system_elapsed_realtime {{.atomTag = android::util::SYSTEM_ELAPSED_REALTIME}, {.coolDownNs = NS_PER_SEC, diff --git a/core/java/android/os/image/DynamicSystemClient.java b/core/java/android/os/image/DynamicSystemClient.java index 921f0f2ab1e2..5cb33615fe22 100644 --- a/core/java/android/os/image/DynamicSystemClient.java +++ b/core/java/android/os/image/DynamicSystemClient.java @@ -256,9 +256,13 @@ public class DynamicSystemClient { mService.send(msg); } catch (RemoteException e) { Slog.e(TAG, "Unable to get status from installation service"); - mExecutor.execute(() -> { + if (mExecutor != null) { + mExecutor.execute(() -> { + mListener.onStatusChanged(STATUS_UNKNOWN, CAUSE_ERROR_IPC, 0, e); + }); + } else { mListener.onStatusChanged(STATUS_UNKNOWN, CAUSE_ERROR_IPC, 0, e); - }); + } } } diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index 9d22d304ad00..c191a0daeb38 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -59,7 +59,6 @@ public class FeatureFlagUtils { DEFAULT_FLAGS.put("settings_wifi_details_datausage_header", "false"); DEFAULT_FLAGS.put("settings_skip_direction_mutable", "true"); DEFAULT_FLAGS.put(SETTINGS_WIFITRACKER2, "false"); - DEFAULT_FLAGS.put("settings_work_profile", "true"); DEFAULT_FLAGS.put("settings_controller_loading_enhancement", "false"); DEFAULT_FLAGS.put("settings_conditionals", "false"); DEFAULT_FLAGS.put(NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "false"); diff --git a/core/java/android/view/ImeFocusController.java b/core/java/android/view/ImeFocusController.java new file mode 100644 index 000000000000..5c494c17669a --- /dev/null +++ b/core/java/android/view/ImeFocusController.java @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2019 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; + +import android.annotation.AnyThread; +import android.annotation.NonNull; +import android.annotation.UiThread; +import android.util.Log; +import android.view.inputmethod.InputMethodManager; + +import com.android.internal.inputmethod.InputMethodDebug; +import com.android.internal.inputmethod.StartInputFlags; +import com.android.internal.inputmethod.StartInputReason; + +/** + * Responsible for IME focus handling inside {@link ViewRootImpl}. + * @hide + */ +public final class ImeFocusController { + private static final boolean DEBUG = false; + private static final String TAG = "ImeFocusController"; + + private final ViewRootImpl mViewRootImpl; + private boolean mHasImeFocus = false; + private View mServedView; + private View mNextServedView; + + @UiThread + ImeFocusController(@NonNull ViewRootImpl viewRootImpl) { + mViewRootImpl = viewRootImpl; + } + + private InputMethodManagerDelegate getImmDelegate() { + return mViewRootImpl.mContext.getSystemService(InputMethodManager.class).getDelegate(); + } + + @UiThread + void onTraversal(boolean hasWindowFocus, WindowManager.LayoutParams windowAttribute) { + final boolean hasImeFocus = updateImeFocusable(windowAttribute, false /* force */); + if (!hasWindowFocus || isInLocalFocusMode(windowAttribute)) { + return; + } + if (hasImeFocus == mHasImeFocus) { + return; + } + mHasImeFocus = hasImeFocus; + if (mHasImeFocus) { + onPreWindowFocus(true /* hasWindowFocus */, windowAttribute); + onPostWindowFocus(mViewRootImpl.mView.findFocus(), true /* hasWindowFocus */, + windowAttribute); + } + } + + @UiThread + void onPreWindowFocus(boolean hasWindowFocus, WindowManager.LayoutParams windowAttribute) { + if (!mHasImeFocus || isInLocalFocusMode(windowAttribute)) { + return; + } + if (hasWindowFocus) { + getImmDelegate().setCurrentRootView(mViewRootImpl); + } + } + + @UiThread + boolean updateImeFocusable(WindowManager.LayoutParams windowAttribute, boolean force) { + final boolean hasImeFocus = WindowManager.LayoutParams.mayUseInputMethod( + windowAttribute.flags); + if (force) { + mHasImeFocus = hasImeFocus; + } + return hasImeFocus; + } + + @UiThread + void onPostWindowFocus(View focusedView, boolean hasWindowFocus, + WindowManager.LayoutParams windowAttribute) { + if (!hasWindowFocus || !mHasImeFocus || isInLocalFocusMode(windowAttribute)) { + return; + } + if (DEBUG) { + Log.v(TAG, "onWindowFocus: " + focusedView + + " softInputMode=" + InputMethodDebug.softInputModeToString( + windowAttribute.softInputMode)); + } + + boolean forceFocus = false; + if (getImmDelegate().isRestartOnNextWindowFocus(true /* reset */)) { + if (DEBUG) Log.v(TAG, "Restarting due to isRestartOnNextWindowFocus as true"); + forceFocus = true; + } + // Update mNextServedView when focusedView changed. + final View viewForWindowFocus = focusedView != null ? focusedView : mViewRootImpl.mView; + onViewFocusChanged(viewForWindowFocus, true); + + getImmDelegate().startInputAsyncOnWindowFocusGain(viewForWindowFocus, + windowAttribute.softInputMode, windowAttribute.flags, forceFocus); + } + + public boolean checkFocus(boolean forceNewFocus, boolean startInput) { + if (!getImmDelegate().isCurrentRootView(mViewRootImpl) + || (mServedView == mNextServedView && !forceNewFocus)) { + return false; + } + if (DEBUG) Log.v(TAG, "checkFocus: view=" + mServedView + + " next=" + mNextServedView + + " force=" + forceNewFocus + + " package=" + + (mServedView != null ? mServedView.getContext().getPackageName() : "<none>")); + + // Close the connection when no next served view coming. + if (mNextServedView == null) { + getImmDelegate().finishInput(); + getImmDelegate().closeCurrentIme(); + return false; + } + mServedView = mNextServedView; + getImmDelegate().finishComposingText(); + + if (startInput) { + getImmDelegate().startInput(StartInputReason.CHECK_FOCUS, null, 0, 0, 0); + } + return true; + } + + @UiThread + void onViewFocusChanged(View view, boolean hasFocus) { + if (view == null || view.isTemporarilyDetached()) { + return; + } + if (!getImmDelegate().isCurrentRootView(view.getViewRootImpl())) { + return; + } + if (mServedView == view || !view.hasImeFocus() || !view.hasWindowFocus()) { + return; + } + mNextServedView = hasFocus ? view : null; + mViewRootImpl.dispatchCheckFocus(); + } + + @UiThread + void onViewDetachedFromWindow(View view) { + if (!getImmDelegate().isCurrentRootView(view.getViewRootImpl())) { + return; + } + if (mServedView == view) { + mNextServedView = null; + mViewRootImpl.dispatchCheckFocus(); + } + } + + @UiThread + void onWindowDismissed() { + if (!getImmDelegate().isCurrentRootView(mViewRootImpl)) { + return; + } + if (mServedView != null) { + getImmDelegate().finishInput(); + } + getImmDelegate().setCurrentRootView(null); + mHasImeFocus = false; + } + + /** + * @param windowAttribute {@link WindowManager.LayoutParams} to be checked. + * @return Whether the window is in local focus mode or not. + */ + @AnyThread + private static boolean isInLocalFocusMode(WindowManager.LayoutParams windowAttribute) { + return (windowAttribute.flags & WindowManager.LayoutParams.FLAG_LOCAL_FOCUS_MODE) != 0; + } + + int onProcessImeInputStage(Object token, InputEvent event, + WindowManager.LayoutParams windowAttribute, + InputMethodManager.FinishedInputEventCallback callback) { + if (!mHasImeFocus || isInLocalFocusMode(windowAttribute)) { + return InputMethodManager.DISPATCH_NOT_HANDLED; + } + final InputMethodManager imm = + mViewRootImpl.mContext.getSystemService(InputMethodManager.class); + if (imm == null) { + return InputMethodManager.DISPATCH_NOT_HANDLED; + } + return imm.dispatchInputEvent(event, token, callback, mViewRootImpl.mHandler); + } + + /** + * A delegate implementing some basic {@link InputMethodManager} APIs. + * @hide + */ + public interface InputMethodManagerDelegate { + boolean startInput(@StartInputReason int startInputReason, View focusedView, + @StartInputFlags int startInputFlags, + @WindowManager.LayoutParams.SoftInputModeFlags int softInputMode, int windowFlags); + void startInputAsyncOnWindowFocusGain(View rootView, + @WindowManager.LayoutParams.SoftInputModeFlags int softInputMode, int windowFlags, + boolean forceNewFocus); + void finishInput(); + void closeCurrentIme(); + void finishComposingText(); + void setCurrentRootView(ViewRootImpl rootView); + boolean isCurrentRootView(ViewRootImpl rootView); + boolean isRestartOnNextWindowFocus(boolean reset); + } + + public View getServedView() { + return mServedView; + } + + public View getNextServedView() { + return mNextServedView; + } + + public void setServedView(View view) { + mServedView = view; + } + + public void setNextServedView(View view) { + mNextServedView = view; + } +} diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 13d609b16541..562ed0eb8e70 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -130,7 +130,6 @@ import android.view.contentcapture.ContentCaptureManager; import android.view.contentcapture.ContentCaptureSession; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; -import android.view.inputmethod.InputMethodManager; import android.view.inspector.InspectableProperty; import android.view.inspector.InspectableProperty.EnumEntry; import android.view.inspector.InspectableProperty.FlagEntry; @@ -7942,12 +7941,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (isPressed()) { setPressed(false); } - if (mAttachInfo != null && mAttachInfo.mHasWindowFocus) { - notifyFocusChangeToInputMethodManager(false /* hasFocus */); + if (hasWindowFocus()) { + notifyFocusChangeToImeFocusController(false /* hasFocus */); } onFocusLost(); - } else if (mAttachInfo != null && mAttachInfo.mHasWindowFocus) { - notifyFocusChangeToInputMethodManager(true /* hasFocus */); + } else if (hasWindowFocus()) { + notifyFocusChangeToImeFocusController(true /* hasFocus */); } invalidate(true); @@ -7964,23 +7963,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * Notify {@link InputMethodManager} about the focus change of the {@link View}. - * - * <p>Does nothing when {@link InputMethodManager} is not available.</p> + * Notify {@link ImeFocusController} about the focus change of the {@link View}. * * @param hasFocus {@code true} when the {@link View} is being focused. */ - private void notifyFocusChangeToInputMethodManager(boolean hasFocus) { - final InputMethodManager imm = - getContext().getSystemService(InputMethodManager.class); - if (imm == null) { + private void notifyFocusChangeToImeFocusController(boolean hasFocus) { + if (mAttachInfo == null) { return; } - if (hasFocus) { - imm.focusIn(this); - } else { - imm.focusOut(this); - } + mAttachInfo.mViewRootImpl.getImeFocusController().onViewFocusChanged(this, hasFocus); } /** @hide */ @@ -13918,7 +13909,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mPrivateFlags3 &= ~PFLAG3_TEMPORARY_DETACH; onFinishTemporaryDetach(); if (hasWindowFocus() && hasFocus()) { - notifyFocusChangeToInputMethodManager(true /* hasFocus */); + notifyFocusChangeToImeFocusController(true /* hasFocus */); } notifyEnterOrExitForAutoFillIfNeeded(true); notifyAppearedOrDisappearedForContentCaptureIfNeeded(true); @@ -14326,13 +14317,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN; if ((mPrivateFlags & PFLAG_FOCUSED) != 0) { - notifyFocusChangeToInputMethodManager(false /* hasFocus */); + notifyFocusChangeToImeFocusController(false /* hasFocus */); } removeLongPressCallback(); removeTapCallback(); onFocusLost(); } else if ((mPrivateFlags & PFLAG_FOCUSED) != 0) { - notifyFocusChangeToInputMethodManager(true /* hasFocus */); + notifyFocusChangeToImeFocusController(true /* hasFocus */); } refreshDrawableState(); @@ -14349,6 +14340,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * @return {@code true} if this view is in a window that currently has IME focusable state. + * @hide + */ + public boolean hasImeFocus() { + return mAttachInfo != null && mAttachInfo.mHasImeFocus; + } + + /** * Dispatch a view visibility change down the view hierarchy. * ViewGroups should override to route to their children. * @param changedView The view whose visibility changed. Could be 'this' or @@ -19644,7 +19643,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, rebuildOutline(); if (isFocused()) { - notifyFocusChangeToInputMethodManager(true /* hasFocus */); + notifyFocusChangeToImeFocusController(true /* hasFocus */); } } @@ -20227,9 +20226,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, onDetachedFromWindow(); onDetachedFromWindowInternal(); - InputMethodManager imm = getContext().getSystemService(InputMethodManager.class); - if (imm != null) { - imm.onViewDetachedFromWindow(this); + if (info != null) { + info.mViewRootImpl.getImeFocusController().onViewDetachedFromWindow(this); } ListenerInfo li = mListenerInfo; @@ -28565,6 +28563,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, boolean mHasWindowFocus; /** + * Indicates whether the view's window has IME focused. + */ + boolean mHasImeFocus; + + /** * The current visibility of the window. */ int mWindowVisibility; diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 2ef944f35982..17b945b71773 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -453,7 +453,6 @@ public final class ViewRootImpl implements ViewParent, boolean mReportNextDraw; boolean mFullRedrawNeeded; boolean mNewSurfaceNeeded; - boolean mLastWasImTarget; boolean mForceNextWindowRelayout; CountDownLatch mWindowDrawCountDown; @@ -619,6 +618,16 @@ public final class ViewRootImpl implements ViewParent, InputEventConsistencyVerifier.isInstrumentationEnabled() ? new InputEventConsistencyVerifier(this, 0) : null; + private final ImeFocusController mImeFocusController; + + /** + * @return {@link ImeFocusController} for this instance. + */ + @NonNull + public ImeFocusController getImeFocusController() { + return mImeFocusController; + } + private final InsetsController mInsetsController = new InsetsController(this); private final GestureExclusionTracker mGestureExclusionTracker = new GestureExclusionTracker(); @@ -704,6 +713,7 @@ public final class ViewRootImpl implements ViewParent, } loadSystemProperties(); + mImeFocusController = new ImeFocusController(this); } public static void addFirstDrawHandler(Runnable callback) { @@ -1054,11 +1064,6 @@ public final class ViewRootImpl implements ViewParent, } } - /** Whether the window is in local focus mode or not */ - private boolean isInLocalFocusMode() { - return (mWindowAttributes.flags & WindowManager.LayoutParams.FLAG_LOCAL_FOCUS_MODE) != 0; - } - @UnsupportedAppUsage public int getWindowFlags() { return mWindowAttributes.flags; @@ -2892,19 +2897,7 @@ public final class ViewRootImpl implements ViewParent, mViewVisibility = viewVisibility; mHadWindowFocus = hasWindowFocus; - if (hasWindowFocus && !isInLocalFocusMode()) { - final boolean imTarget = WindowManager.LayoutParams - .mayUseInputMethod(mWindowAttributes.flags); - if (imTarget != mLastWasImTarget) { - mLastWasImTarget = imTarget; - InputMethodManager imm = mContext.getSystemService(InputMethodManager.class); - if (imm != null && imTarget) { - imm.onPreWindowFocus(mView, hasWindowFocus); - imm.onPostWindowFocus(mView, mView.findFocus(), - mWindowAttributes.softInputMode, mWindowAttributes.flags); - } - } - } + mImeFocusController.onTraversal(hasWindowFocus, mWindowAttributes); // Remember if we must report the next draw. if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) { @@ -3072,14 +3065,10 @@ public final class ViewRootImpl implements ViewParent, } mAttachInfo.mHasWindowFocus = hasWindowFocus; + mAttachInfo.mHasImeFocus = mImeFocusController.updateImeFocusable( + mWindowAttributes, true /* force */); + mImeFocusController.onPreWindowFocus(hasWindowFocus, mWindowAttributes); - mLastWasImTarget = WindowManager.LayoutParams - .mayUseInputMethod(mWindowAttributes.flags); - - InputMethodManager imm = mContext.getSystemService(InputMethodManager.class); - if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) { - imm.onPreWindowFocus(mView, hasWindowFocus); - } if (mView != null) { mAttachInfo.mKeyDispatchState.reset(); mView.dispatchWindowFocusChanged(hasWindowFocus); @@ -3091,11 +3080,10 @@ public final class ViewRootImpl implements ViewParent, // Note: must be done after the focus change callbacks, // so all of the view state is set up correctly. + mImeFocusController.onPostWindowFocus(mView.findFocus(), hasWindowFocus, + mWindowAttributes); + if (hasWindowFocus) { - if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) { - imm.onPostWindowFocus(mView, mView.findFocus(), - mWindowAttributes.softInputMode, mWindowAttributes.flags); - } // Clear the forward bit. We can just do this directly, since // the window manager doesn't care about it. mWindowAttributes.softInputMode &= @@ -4891,10 +4879,7 @@ public final class ViewRootImpl implements ViewParent, enqueueInputEvent(event, null, 0, true); } break; case MSG_CHECK_FOCUS: { - InputMethodManager imm = mContext.getSystemService(InputMethodManager.class); - if (imm != null) { - imm.checkFocus(); - } + getImeFocusController().checkFocus(false, true); } break; case MSG_CLOSE_SYSTEM_DIALOGS: { if (mView != null) { @@ -5458,23 +5443,20 @@ public final class ViewRootImpl implements ViewParent, @Override protected int onProcess(QueuedInputEvent q) { - if (mLastWasImTarget && !isInLocalFocusMode()) { - InputMethodManager imm = mContext.getSystemService(InputMethodManager.class); - if (imm != null) { - final InputEvent event = q.mEvent; - if (DEBUG_IMF) Log.v(mTag, "Sending input event to IME: " + event); - int result = imm.dispatchInputEvent(event, q, this, mHandler); - if (result == InputMethodManager.DISPATCH_HANDLED) { - return FINISH_HANDLED; - } else if (result == InputMethodManager.DISPATCH_NOT_HANDLED) { - // The IME could not handle it, so skip along to the next InputStage - return FORWARD; - } else { - return DEFER; // callback will be invoked later - } - } + final int result = mImeFocusController.onProcessImeInputStage( + q, q.mEvent, mWindowAttributes, this); + switch (result) { + case InputMethodManager.DISPATCH_IN_PROGRESS: + // callback will be invoked later + return DEFER; + case InputMethodManager.DISPATCH_NOT_HANDLED: + // The IME could not handle it, so skip along to the next InputStage + return FORWARD; + case InputMethodManager.DISPATCH_HANDLED: + return FINISH_HANDLED; + default: + throw new IllegalStateException("Unexpected result=" + result); } - return FORWARD; } @Override diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java index 7d5564e1c8be..ccfbd7e5c1dc 100644 --- a/core/java/android/view/WindowManagerGlobal.java +++ b/core/java/android/view/WindowManagerGlobal.java @@ -487,11 +487,8 @@ public final class WindowManagerGlobal { ViewRootImpl root = mRoots.get(index); View view = root.getView(); - if (view != null) { - InputMethodManager imm = view.getContext().getSystemService(InputMethodManager.class); - if (imm != null) { - imm.windowDismissed(mViews.get(index).getWindowToken()); - } + if (root != null) { + root.getImeFocusController().onWindowDismissed(); } boolean deferred = root.die(immediate); if (view != null) { diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java index ae2fb8ebc24a..d5d631ac1dc7 100644 --- a/core/java/android/view/inputmethod/BaseInputConnection.java +++ b/core/java/android/view/inputmethod/BaseInputConnection.java @@ -758,8 +758,9 @@ public class BaseInputConnection implements InputConnection { Context context; if (mTargetView != null) { context = mTargetView.getContext(); - } else if (mIMM.mServedView != null) { - context = mIMM.mServedView.getContext(); + } else if (mIMM.mCurRootView != null) { + final View servedView = mIMM.mCurRootView.getImeFocusController().getServedView(); + context = servedView != null ? servedView.getContext() : null; } else { context = null; } diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index f3007a794344..904e736d214e 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -64,6 +64,7 @@ import android.view.InputChannel; import android.view.InputEvent; import android.view.InputEventSender; import android.view.KeyEvent; +import android.view.ImeFocusController; import android.view.View; import android.view.ViewRootImpl; import android.view.WindowManager.LayoutParams.SoftInputModeFlags; @@ -364,10 +365,10 @@ public final class InputMethodManager { boolean mActive = false; /** - * {@code true} if next {@link #onPostWindowFocus(View, View, int, int)} needs to + * {@code true} if next {@link ImeFocusController#onPostWindowFocus} needs to * restart input. */ - boolean mRestartOnNextWindowFocus = true; + private boolean mRestartOnNextWindowFocus = true; /** * As reported by IME through InputConnection. @@ -380,22 +381,8 @@ public final class InputMethodManager { * This is the root view of the overall window that currently has input * method focus. */ - @UnsupportedAppUsage - View mCurRootView; - /** - * This is the view that should currently be served by an input method, - * regardless of the state of setting that up. - */ - // See comment to mH field in regard to @UnsupportedAppUsage - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) - View mServedView; - /** - * This is then next view that will be served by the input method, when - * we get around to updating things. - */ - // See comment to mH field in regard to @UnsupportedAppUsage - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) - View mNextServedView; + @GuardedBy("mH") + ViewRootImpl mCurRootView; /** * This is set when we are in the process of connecting, to determine * when we have actually finished. @@ -489,6 +476,8 @@ public final class InputMethodManager { final Pool<PendingEvent> mPendingEventPool = new SimplePool<>(20); final SparseArray<PendingEvent> mPendingEvents = new SparseArray<>(20); + final DelegateImpl mDelegate = new DelegateImpl(); + // ----------------------------------------------------------- static final int MSG_DUMP = 1; @@ -564,6 +553,178 @@ public final class InputMethodManager { return servedView.hasWindowFocus() || isAutofillUIShowing(servedView); } + private final class DelegateImpl implements + ImeFocusController.InputMethodManagerDelegate { + /** + * Used by {@link ImeFocusController} to start input connection. + */ + @Override + public boolean startInput(@StartInputReason int startInputReason, View focusedView, + @StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode, + int windowFlags) { + synchronized (mH) { + mCurrentTextBoxAttribute = null; + mCompletions = null; + mServedConnecting = true; + if (getServedViewLocked() != null && !getServedViewLocked().onCheckIsTextEditor()) { + // servedView has changed and it's not editable. + maybeCallServedViewChangedLocked(null); + } + } + return startInputInner(startInputReason, + focusedView != null ? focusedView.getWindowToken() : null, startInputFlags, + softInputMode, windowFlags); + } + + /** + * Used by {@link ImeFocusController} to finish input connection. + */ + @Override + public void finishInput() { + synchronized (mH) { + finishInputLocked(); + } + } + + /** + * Used by {@link ImeFocusController} to hide current input method editor. + */ + @Override + public void closeCurrentIme() { + closeCurrentInput(); + } + + /** + * For {@link ImeFocusController} to start input asynchronously when focus gain. + */ + @Override + public void startInputAsyncOnWindowFocusGain(View focusedView, + @SoftInputModeFlags int softInputMode, int windowFlags, boolean forceNewFocus) { + final boolean forceNewFocus1 = forceNewFocus; + final int startInputFlags = getStartInputFlags(focusedView, 0); + + if (mWindowFocusGainFuture != null) { + mWindowFocusGainFuture.cancel(false /* mayInterruptIfRunning */); + } + mWindowFocusGainFuture = mStartInputWorker.submit(() -> { + synchronized (mH) { + if (mCurRootView == null) { + return; + } + if (mCurRootView.getImeFocusController().checkFocus(forceNewFocus1, false)) { + // We need to restart input on the current focus view. This + // should be done in conjunction with telling the system service + // about the window gaining focus, to help make the transition + // smooth. + if (startInput(StartInputReason.WINDOW_FOCUS_GAIN, + focusedView, startInputFlags, softInputMode, windowFlags)) { + return; + } + } + + // For some reason we didn't do a startInput + windowFocusGain, so + // we'll just do a window focus gain and call it a day. + try { + if (DEBUG) Log.v(TAG, "Reporting focus gain, without startInput"); + mService.startInputOrWindowGainedFocus( + StartInputReason.WINDOW_FOCUS_GAIN_REPORT_ONLY, mClient, + focusedView.getWindowToken(), startInputFlags, softInputMode, + windowFlags, + null, null, 0 /* missingMethodFlags */, + mCurRootView.mContext.getApplicationInfo().targetSdkVersion); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + }); + } + + /** + * Used by {@link ImeFocusController} to finish current composing text. + */ + @Override + public void finishComposingText() { + if (mServedInputConnectionWrapper != null) { + mServedInputConnectionWrapper.finishComposingText(); + } + } + + /** + * Used for {@link ImeFocusController} to set the current focused root view. + */ + @Override + public void setCurrentRootView(ViewRootImpl rootView) { + // If the mCurRootView is losing window focus, release the strong reference to it + // so as not to prevent it from being garbage-collected. + if (mWindowFocusGainFuture != null) { + mWindowFocusGainFuture.cancel(false /* mayInterruptIfRunning */); + mWindowFocusGainFuture = null; + } + synchronized (mH) { + mCurRootView = rootView; + } + } + + /** + * Used for {@link ImeFocusController} to return if the root view from the + * controller is this {@link InputMethodManager} currently focused. + * TODO: Address event-order problem when get current root view in multi-threads. + */ + @Override + public boolean isCurrentRootView(ViewRootImpl rootView) { + synchronized (mH) { + return mCurRootView == rootView; + } + } + + /** + * For {@link ImeFocusController#checkFocus} if needed to force check new focus. + */ + @Override + public boolean isRestartOnNextWindowFocus(boolean reset) { + final boolean result = mRestartOnNextWindowFocus; + if (reset) { + mRestartOnNextWindowFocus = false; + } + return result; + } + } + + /** @hide */ + public DelegateImpl getDelegate() { + return mDelegate; + } + + private View getServedViewLocked() { + return mCurRootView != null ? mCurRootView.getImeFocusController().getServedView() : null; + } + + private View getNextServedViewLocked() { + return mCurRootView != null ? mCurRootView.getImeFocusController().getNextServedView() + : null; + } + + private void setServedViewLocked(View view) { + if (mCurRootView != null) { + mCurRootView.getImeFocusController().setServedView(view); + } + } + + private void setNextServedViewLocked(View view) { + if (mCurRootView != null) { + mCurRootView.getImeFocusController().setNextServedView(view); + } + } + + /** + * Returns {@code true} when the given view has been served by Input Method. + */ + private boolean hasServedByInputMethodLocked(View view) { + final View servedView = getServedViewLocked(); + return (servedView == view + || (servedView != null && servedView.checkInputConnectionProxy(view))); + } + class H extends Handler { H(Looper looper) { super(looper, null, true); @@ -629,7 +790,8 @@ public final class InputMethodManager { clearBindingLocked(); // If we were actively using the last input method, then // we would like to re-connect to the next input method. - if (mServedView != null && mServedView.isFocused()) { + final View servedView = getServedViewLocked(); + if (servedView != null && servedView.isFocused()) { mServedConnecting = true; } startInput = mActive; @@ -664,11 +826,16 @@ public final class InputMethodManager { } // Check focus again in case that "onWindowFocus" is called before // handling this message. - if (mServedView != null && canStartInput(mServedView)) { - if (checkFocusNoStartInput(mRestartOnNextWindowFocus)) { + final View servedView; + synchronized (mH) { + servedView = getServedViewLocked(); + } + if (servedView != null && canStartInput(servedView)) { + if (mCurRootView != null && mCurRootView.getImeFocusController() + .checkFocus(mRestartOnNextWindowFocus, false)) { final int reason = active ? StartInputReason.ACTIVATED_BY_IMMS : StartInputReason.DEACTIVATED_BY_IMMS; - startInputInner(reason, null, 0, 0, 0); + mDelegate.startInput(reason, null, 0, 0, 0); } } return; @@ -1212,10 +1379,7 @@ public final class InputMethodManager { checkFocus(); synchronized (mH) { - return (mServedView == view - || (mServedView != null - && mServedView.checkInputConnectionProxy(view))) - && mCurrentTextBoxAttribute != null; + return hasServedByInputMethodLocked(view) && mCurrentTextBoxAttribute != null; } } @@ -1225,7 +1389,7 @@ public final class InputMethodManager { public boolean isActive() { checkFocus(); synchronized (mH) { - return mServedView != null && mCurrentTextBoxAttribute != null; + return getServedViewLocked() != null && mCurrentTextBoxAttribute != null; } } @@ -1286,11 +1450,14 @@ public final class InputMethodManager { */ @UnsupportedAppUsage void finishInputLocked() { - mNextServedView = null; mActivityViewToScreenMatrix = null; - if (mServedView != null) { - if (DEBUG) Log.v(TAG, "FINISH INPUT: mServedView=" + dumpViewInfo(mServedView)); - mServedView = null; + setNextServedViewLocked(null); + if (getServedViewLocked() != null) { + if (DEBUG) { + Log.v(TAG, "FINISH INPUT: mServedView=" + + dumpViewInfo(getServedViewLocked())); + } + setServedViewLocked(null); mCompletions = null; mServedConnecting = false; clearConnectionLocked(); @@ -1307,8 +1474,7 @@ public final class InputMethodManager { checkFocus(); synchronized (mH) { - if (mServedView != view && (mServedView == null - || !mServedView.checkInputConnectionProxy(view))) { + if (!hasServedByInputMethodLocked(view)) { return; } @@ -1332,8 +1498,7 @@ public final class InputMethodManager { checkFocus(); synchronized (mH) { - if (mServedView != view && (mServedView == null - || !mServedView.checkInputConnectionProxy(view))) { + if (!hasServedByInputMethodLocked(view)) { return; } @@ -1447,8 +1612,7 @@ public final class InputMethodManager { checkFocus(); synchronized (mH) { - if (mServedView != view && (mServedView == null - || !mServedView.checkInputConnectionProxy(view))) { + if (!hasServedByInputMethodLocked(view)) { return false; } @@ -1539,7 +1703,8 @@ public final class InputMethodManager { ResultReceiver resultReceiver) { checkFocus(); synchronized (mH) { - if (mServedView == null || mServedView.getWindowToken() != windowToken) { + final View servedView = getServedViewLocked(); + if (servedView == null || servedView.getWindowToken() != windowToken) { return false; } @@ -1566,7 +1731,8 @@ public final class InputMethodManager { **/ public void toggleSoftInputFromWindow(IBinder windowToken, int showFlags, int hideFlags) { synchronized (mH) { - if (mServedView == null || mServedView.getWindowToken() != windowToken) { + final View servedView = getServedViewLocked(); + if (servedView == null || servedView.getWindowToken() != windowToken) { return; } if (mCurMethod != null) { @@ -1617,8 +1783,7 @@ public final class InputMethodManager { checkFocus(); synchronized (mH) { - if (mServedView != view && (mServedView == null - || !mServedView.checkInputConnectionProxy(view))) { + if (!hasServedByInputMethodLocked(view)) { return; } @@ -1645,7 +1810,7 @@ public final class InputMethodManager { final View view; synchronized (mH) { - view = mServedView; + view = getServedViewLocked(); // Make sure we have a window token for the served view. if (DEBUG) { @@ -1664,10 +1829,7 @@ public final class InputMethodManager { Log.e(TAG, "ABORT input: ServedView must be attached to a Window"); return false; } - startInputFlags |= StartInputFlags.VIEW_HAS_FOCUS; - if (view.onCheckIsTextEditor()) { - startInputFlags |= StartInputFlags.IS_TEXT_EDITOR; - } + startInputFlags = getStartInputFlags(view, startInputFlags); softInputMode = view.getViewRootImpl().mWindowAttributes.softInputMode; windowFlags = view.getViewRootImpl().mWindowAttributes.flags; } @@ -1690,7 +1852,7 @@ public final class InputMethodManager { // The view is running on a different thread than our own, so // we need to reschedule our work for over there. if (DEBUG) Log.v(TAG, "Starting input: reschedule to view thread"); - vh.post(() -> startInputInner(startInputReason, null, 0, 0, 0)); + vh.post(() -> mDelegate.startInput(startInputReason, null, 0, 0, 0)); return false; } @@ -1709,11 +1871,12 @@ public final class InputMethodManager { synchronized (mH) { // Now that we are locked again, validate that our state hasn't // changed. - if (mServedView != view || !mServedConnecting) { + final View servedView = getServedViewLocked(); + if (servedView != view || !mServedConnecting) { // Something else happened, so abort. if (DEBUG) Log.v(TAG, "Starting input: finished by someone else. view=" + dumpViewInfo(view) - + " mServedView=" + dumpViewInfo(mServedView) + + " servedView=" + dumpViewInfo(servedView) + " mServedConnecting=" + mServedConnecting); return false; } @@ -1804,101 +1967,12 @@ public final class InputMethodManager { return true; } - /** - * When the focused window is dismissed, this method is called to finish the - * input method started before. - * @hide - */ - @UnsupportedAppUsage - public void windowDismissed(IBinder appWindowToken) { - checkFocus(); - synchronized (mH) { - if (mServedView != null && - mServedView.getWindowToken() == appWindowToken) { - finishInputLocked(); - } - if (mCurRootView != null && - mCurRootView.getWindowToken() == appWindowToken) { - mCurRootView = null; - } - } - } - - /** - * Call this when a view receives focus. - * @hide - */ - @UnsupportedAppUsage - public void focusIn(View view) { - synchronized (mH) { - focusInLocked(view); - } - } - - void focusInLocked(View view) { - if (DEBUG) Log.v(TAG, "focusIn: " + dumpViewInfo(view)); - - if (view != null && view.isTemporarilyDetached()) { - // This is a request from a view that is temporarily detached from a window. - if (DEBUG) Log.v(TAG, "Temporarily detached view, ignoring"); - return; - } - - if (mCurRootView != view.getRootView()) { - // This is a request from a window that isn't in the window with - // IME focus, so ignore it. - if (DEBUG) Log.v(TAG, "Not IME target window, ignoring"); - return; - } - - mNextServedView = view; - scheduleCheckFocusLocked(view); - } - - /** - * Call this when a view loses focus. - * @hide - */ - @UnsupportedAppUsage - public void focusOut(View view) { - synchronized (mH) { - if (DEBUG) Log.v(TAG, "focusOut: view=" + dumpViewInfo(view) - + " mServedView=" + dumpViewInfo(mServedView)); - if (mServedView != view) { - // The following code would auto-hide the IME if we end up - // with no more views with focus. This can happen, however, - // whenever we go into touch mode, so it ends up hiding - // at times when we don't really want it to. For now it - // seems better to just turn it all off. - // TODO: Check view.isTemporarilyDetached() when re-enable the following code. - if (false && canStartInput(view)) { - mNextServedView = null; - scheduleCheckFocusLocked(view); - } - } - } - } - - /** - * Call this when a view is being detached from a {@link android.view.Window}. - * @hide - */ - public void onViewDetachedFromWindow(View view) { - synchronized (mH) { - if (DEBUG) Log.v(TAG, "onViewDetachedFromWindow: view=" + dumpViewInfo(view) - + " mServedView=" + dumpViewInfo(mServedView)); - if (mServedView == view) { - mNextServedView = null; - scheduleCheckFocusLocked(view); - } - } - } - - static void scheduleCheckFocusLocked(View view) { - ViewRootImpl viewRootImpl = view.getViewRootImpl(); - if (viewRootImpl != null) { - viewRootImpl.dispatchCheckFocus(); + private int getStartInputFlags(View focusedView, int startInputFlags) { + startInputFlags |= StartInputFlags.VIEW_HAS_FOCUS; + if (focusedView.onCheckIsTextEditor()) { + startInputFlags |= StartInputFlags.IS_TEXT_EDITOR; } + return startInputFlags; } /** @@ -1906,54 +1980,12 @@ public final class InputMethodManager { */ @UnsupportedAppUsage public void checkFocus() { - if (checkFocusNoStartInput(false)) { - startInputInner(StartInputReason.CHECK_FOCUS, null, 0, 0, 0); - } - } - - private boolean checkFocusNoStartInput(boolean forceNewFocus) { - // This is called a lot, so short-circuit before locking. - if (mServedView == mNextServedView && !forceNewFocus) { - return false; - } - - final ControlledInputConnectionWrapper ic; synchronized (mH) { - if (mServedView == mNextServedView && !forceNewFocus) { - return false; - } - if (DEBUG) Log.v(TAG, "checkFocus: view=" + mServedView - + " next=" + mNextServedView - + " forceNewFocus=" + forceNewFocus - + " package=" - + (mServedView != null ? mServedView.getContext().getPackageName() : "<none>")); - - if (mNextServedView == null) { - finishInputLocked(); - // In this case, we used to have a focused view on the window, - // but no longer do. We should make sure the input method is - // no longer shown, since it serves no purpose. - closeCurrentInput(); - return false; - } - - ic = mServedInputConnectionWrapper; - - mServedView = mNextServedView; - mCurrentTextBoxAttribute = null; - mCompletions = null; - mServedConnecting = true; - // servedView has changed and it's not editable. - if (!mServedView.onCheckIsTextEditor()) { - maybeCallServedViewChangedLocked(null); + if (mCurRootView != null) { + mCurRootView.getImeFocusController().checkFocus(false /* forceNewFocus */, + true /* startInput */); } } - - if (ic != null) { - ic.finishComposingText(); - } - - return true; } @UnsupportedAppUsage @@ -1966,92 +1998,6 @@ public final class InputMethodManager { } /** - * Called by ViewAncestor when its window gets input focus. - * @hide - */ - public void onPostWindowFocus(View rootView, View focusedView, - @SoftInputModeFlags int softInputMode, int windowFlags) { - boolean forceNewFocus = false; - synchronized (mH) { - if (DEBUG) Log.v(TAG, "onWindowFocus: " + focusedView - + " softInputMode=" + InputMethodDebug.softInputModeToString(softInputMode) - + " flags=#" + Integer.toHexString(windowFlags)); - if (mRestartOnNextWindowFocus) { - if (DEBUG) Log.v(TAG, "Restarting due to mRestartOnNextWindowFocus"); - mRestartOnNextWindowFocus = false; - forceNewFocus = true; - } - focusInLocked(focusedView != null ? focusedView : rootView); - } - - int startInputFlags = 0; - if (focusedView != null) { - startInputFlags |= StartInputFlags.VIEW_HAS_FOCUS; - if (focusedView.onCheckIsTextEditor()) { - startInputFlags |= StartInputFlags.IS_TEXT_EDITOR; - } - } - - final boolean forceNewFocus1 = forceNewFocus; - final int startInputFlags1 = startInputFlags; - if (mWindowFocusGainFuture != null) { - mWindowFocusGainFuture.cancel(false/* mayInterruptIfRunning */); - } - mWindowFocusGainFuture = mStartInputWorker.submit(() -> { - if (checkFocusNoStartInput(forceNewFocus1)) { - // We need to restart input on the current focus view. This - // should be done in conjunction with telling the system service - // about the window gaining focus, to help make the transition - // smooth. - if (startInputInner(StartInputReason.WINDOW_FOCUS_GAIN, rootView.getWindowToken(), - startInputFlags1, softInputMode, windowFlags)) { - return; - } - } - - // For some reason we didn't do a startInput + windowFocusGain, so - // we'll just do a window focus gain and call it a day. - synchronized (mH) { - try { - if (DEBUG) Log.v(TAG, "Reporting focus gain, without startInput"); - mService.startInputOrWindowGainedFocus( - StartInputReason.WINDOW_FOCUS_GAIN_REPORT_ONLY, mClient, - rootView.getWindowToken(), startInputFlags1, softInputMode, windowFlags, - null, null, 0 /* missingMethodFlags */, - rootView.getContext().getApplicationInfo().targetSdkVersion); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - }); - } - - /** @hide */ - @UnsupportedAppUsage - public void onPreWindowFocus(View rootView, boolean hasWindowFocus) { - synchronized (mH) { - if (rootView == null) { - mCurRootView = null; - } if (hasWindowFocus) { - mCurRootView = rootView; - } else if (rootView == mCurRootView) { - // If the mCurRootView is losing window focus, release the strong reference to it - // so as not to prevent it from being garbage-collected. - mCurRootView = null; - if (mWindowFocusGainFuture != null) { - mWindowFocusGainFuture.cancel(false /* mayInterruptIfRunning */); - mWindowFocusGainFuture = null; - } - } else { - if (DEBUG) { - Log.v(TAG, "Ignoring onPreWindowFocus()." - + " mCurRootView=" + mCurRootView + " rootView=" + rootView); - } - } - } - } - - /** * Register for IME state callbacks and applying visibility in * {@link android.view.ImeInsetsSourceConsumer}. * @hide @@ -2090,10 +2036,11 @@ public final class InputMethodManager { */ public boolean requestImeShow(ResultReceiver resultReceiver) { synchronized (mH) { - if (mServedView == null) { + final View servedView = getServedViewLocked(); + if (servedView == null) { return false; } - showSoftInput(mServedView, 0 /* flags */, resultReceiver); + showSoftInput(servedView, 0 /* flags */, resultReceiver); return true; } } @@ -2135,9 +2082,8 @@ public final class InputMethodManager { checkFocus(); synchronized (mH) { - if ((mServedView != view && (mServedView == null - || !mServedView.checkInputConnectionProxy(view))) - || mCurrentTextBoxAttribute == null || mCurMethod == null) { + if (!hasServedByInputMethodLocked(view) || mCurrentTextBoxAttribute == null + || mCurMethod == null) { return; } @@ -2185,12 +2131,17 @@ public final class InputMethodManager { return; } - final boolean focusChanged = mServedView != mNextServedView; + final View servedView; + final View nextServedView; + synchronized (mH) { + servedView = getServedViewLocked(); + nextServedView = getNextServedViewLocked(); + } + final boolean focusChanged = servedView != nextServedView; checkFocus(); synchronized (mH) { - if ((mServedView != view && (mServedView == null - || !mServedView.checkInputConnectionProxy(view))) - || mCurrentTextBoxAttribute == null || mCurMethod == null) { + if (!hasServedByInputMethodLocked(view) || mCurrentTextBoxAttribute == null + || mCurMethod == null) { return; } try { @@ -2258,9 +2209,8 @@ public final class InputMethodManager { checkFocus(); synchronized (mH) { - if ((mServedView != view && (mServedView == null - || !mServedView.checkInputConnectionProxy(view))) - || mCurrentTextBoxAttribute == null || mCurMethod == null) { + if (!hasServedByInputMethodLocked(view) || mCurrentTextBoxAttribute == null + || mCurMethod == null) { return; } @@ -2296,9 +2246,8 @@ public final class InputMethodManager { checkFocus(); synchronized (mH) { - if ((mServedView != view && - (mServedView == null || !mServedView.checkInputConnectionProxy(view))) - || mCurrentTextBoxAttribute == null || mCurMethod == null) { + if (!hasServedByInputMethodLocked(view) || mCurrentTextBoxAttribute == null + || mCurMethod == null) { return; } // If immediate bit is set, we will call updateCursorAnchorInfo() even when the data has @@ -2354,9 +2303,8 @@ public final class InputMethodManager { checkFocus(); synchronized (mH) { - if ((mServedView != view && (mServedView == null - || !mServedView.checkInputConnectionProxy(view))) - || mCurrentTextBoxAttribute == null || mCurMethod == null) { + if (!hasServedByInputMethodLocked(view) || mCurrentTextBoxAttribute == null + || mCurMethod == null) { return; } try { @@ -2577,8 +2525,9 @@ public final class InputMethodManager { synchronized (mH) { ViewRootImpl viewRootImpl = targetView != null ? targetView.getViewRootImpl() : null; if (viewRootImpl == null) { - if (mServedView != null) { - viewRootImpl = mServedView.getViewRootImpl(); + final View servedView = getServedViewLocked(); + if (servedView != null) { + viewRootImpl = servedView.getViewRootImpl(); } } if (viewRootImpl != null) { @@ -2759,6 +2708,7 @@ public final class InputMethodManager { /** * Show the settings for enabling subtypes of the specified input method. + * * @param imiId An input method, whose subtypes settings will be shown. If imiId is null, * subtypes of all input methods will be shown. */ @@ -3057,8 +3007,8 @@ public final class InputMethodManager { p.println(" mFullscreenMode=" + mFullscreenMode); p.println(" mCurMethod=" + mCurMethod); p.println(" mCurRootView=" + mCurRootView); - p.println(" mServedView=" + mServedView); - p.println(" mNextServedView=" + mNextServedView); + p.println(" mServedView=" + getServedViewLocked()); + p.println(" mNextServedView=" + getNextServedViewLocked()); p.println(" mServedConnecting=" + mServedConnecting); if (mCurrentTextBoxAttribute != null) { p.println(" mCurrentTextBoxAttribute:"); @@ -3134,6 +3084,8 @@ public final class InputMethodManager { sb.append(",window=" + view.getWindowToken()); sb.append(",displayId=" + view.getContext().getDisplayId()); sb.append(",temporaryDetach=" + view.isTemporarilyDetached()); + sb.append(",hasImeFocus=" + view.hasImeFocus()); + return sb.toString(); } } diff --git a/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java b/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java index 751bb6a70880..127d00c0afe2 100644 --- a/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java +++ b/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java @@ -110,7 +110,6 @@ public class GnssMetrics { * Logs the status of a location report received from the HAL */ public void logReceivedLocationStatus(boolean isSuccessful) { - StatsLog.write(StatsLog.GPS_LOCATION_STATUS_REPORTED, isSuccessful); if (!isSuccessful) { mLocationFailureStatistics.addItem(1.0); return; @@ -127,7 +126,6 @@ public class GnssMetrics { DEFAULT_TIME_BETWEEN_FIXES_MILLISECS, desiredTimeBetweenFixesMilliSeconds)) - 1; if (numReportMissed > 0) { for (int i = 0; i < numReportMissed; i++) { - StatsLog.write(StatsLog.GPS_LOCATION_STATUS_REPORTED, false); mLocationFailureStatistics.addItem(1.0); } } @@ -138,7 +136,6 @@ public class GnssMetrics { */ public void logTimeToFirstFixMilliSecs(int timeToFirstFixMilliSeconds) { mTimeToFirstFixSecStatistics.addItem((double) (timeToFirstFixMilliSeconds / 1000)); - StatsLog.write(StatsLog.GPS_TIME_TO_FIRST_FIX_REPORTED, timeToFirstFixMilliSeconds); } /** diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java index ece53353c871..8cd3c6e64b78 100644 --- a/media/java/android/media/AudioAttributes.java +++ b/media/java/android/media/AudioAttributes.java @@ -598,6 +598,11 @@ public final class AudioAttributes implements Parcelable { private boolean mMuteHapticChannels = true; private HashSet<String> mTags = new HashSet<String>(); private Bundle mBundle; + private int mPrivacySensitive = PRIVACY_SENSITIVE_DEFAULT; + + private static final int PRIVACY_SENSITIVE_DEFAULT = -1; + private static final int PRIVACY_SENSITIVE_DISABLED = 0; + private static final int PRIVACY_SENSITIVE_ENABLED = 1; /** * Constructs a new Builder with the defaults. @@ -638,11 +643,20 @@ public final class AudioAttributes implements Parcelable { if (mMuteHapticChannels) { aa.mFlags |= FLAG_MUTE_HAPTIC; } - // capturing for camcorder of communication is private by default to - // reflect legacy behavior - if (aa.mSource == MediaRecorder.AudioSource.VOICE_COMMUNICATION - || aa.mSource == MediaRecorder.AudioSource.CAMCORDER) { + + if (mPrivacySensitive == PRIVACY_SENSITIVE_DEFAULT) { + // capturing for camcorder or communication is private by default to + // reflect legacy behavior + if (mSource == MediaRecorder.AudioSource.VOICE_COMMUNICATION + || mSource == MediaRecorder.AudioSource.CAMCORDER) { + aa.mFlags |= FLAG_CAPTURE_PRIVATE; + } else { + aa.mFlags &= ~FLAG_CAPTURE_PRIVATE; + } + } else if (mPrivacySensitive == PRIVACY_SENSITIVE_ENABLED) { aa.mFlags |= FLAG_CAPTURE_PRIVATE; + } else { + aa.mFlags &= ~FLAG_CAPTURE_PRIVATE; } aa.mTags = (HashSet<String>) mTags.clone(); aa.mFormattedTags = TextUtils.join(";", mTags); @@ -967,6 +981,20 @@ public final class AudioAttributes implements Parcelable { mMuteHapticChannels = muted; return this; } + + /** + * @hide + * Indicates if an AudioRecord build with this AudioAttributes is privacy sensitive or not. + * See {@link AudioRecord.Builder#setPrivacySensitive(boolean)}. + * @param privacySensitive True if capture must be marked as privacy sensitive, + * false otherwise. + * @return the same Builder instance. + */ + public @NonNull Builder setPrivacySensitive(boolean privacySensitive) { + mPrivacySensitive = + privacySensitive ? PRIVACY_SENSITIVE_ENABLED : PRIVACY_SENSITIVE_DISABLED; + return this; + } }; @Override diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java index fd3523de4d0f..4d26b8d3f7af 100644 --- a/media/java/android/media/AudioRecord.java +++ b/media/java/android/media/AudioRecord.java @@ -315,7 +315,7 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection, * @hide * Class constructor with {@link AudioAttributes} and {@link AudioFormat}. * @param attributes a non-null {@link AudioAttributes} instance. Use - * {@link AudioAttributes.Builder#setAudioSource(int)} for configuring the audio + * {@link AudioAttributes.Builder#setCapturePreset(int)} for configuring the audio * source for this instance. * @param format a non-null {@link AudioFormat} instance describing the format of the data * that will be recorded through this AudioRecord. See {@link AudioFormat.Builder} for @@ -754,17 +754,10 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection, "Cannot request private capture with source: " + source); } - int flags = mAttributes.getAllFlags(); - if (mPrivacySensitive == PRIVACY_SENSITIVE_DISABLED) { - flags &= ~AudioAttributes.FLAG_CAPTURE_PRIVATE; - } else if (mPrivacySensitive == PRIVACY_SENSITIVE_ENABLED) { - flags |= AudioAttributes.FLAG_CAPTURE_PRIVATE; - } - if (flags != mAttributes.getAllFlags()) { - mAttributes = new AudioAttributes.Builder(mAttributes) - .replaceFlags(flags) - .build(); - } + mAttributes = new AudioAttributes.Builder(mAttributes) + .setInternalCapturePreset(source) + .setPrivacySensitive(mPrivacySensitive == PRIVACY_SENSITIVE_ENABLED) + .build(); } try { diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java index beb3affa5c9d..9ccb837cf613 100644 --- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java +++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java @@ -417,7 +417,9 @@ public class DynamicSystemInstallationService extends Service break; case STATUS_READY: - builder.setContentText(getString(R.string.notification_install_completed)); + String msgCompleted = getString(R.string.notification_install_completed); + builder.setContentText(msgCompleted) + .setStyle(new Notification.BigTextStyle().bigText(msgCompleted)); builder.addAction(new Notification.Action.Builder( null, getString(R.string.notification_action_discard), @@ -430,7 +432,9 @@ public class DynamicSystemInstallationService extends Service break; case STATUS_IN_USE: - builder.setContentText(getString(R.string.notification_dynsystem_in_use)); + String msgInUse = getString(R.string.notification_dynsystem_in_use); + builder.setContentText(msgInUse) + .setStyle(new Notification.BigTextStyle().bigText(msgInUse)); builder.addAction(new Notification.Action.Builder( null, getString(R.string.notification_action_uninstall), @@ -460,7 +464,49 @@ public class DynamicSystemInstallationService extends Service } private void postStatus(int status, int cause, Throwable detail) { - Log.d(TAG, "postStatus(): statusCode=" + status + ", causeCode=" + cause); + String statusString; + String causeString; + + switch (status) { + case STATUS_NOT_STARTED: + statusString = "NOT_STARTED"; + break; + case STATUS_IN_PROGRESS: + statusString = "IN_PROGRESS"; + break; + case STATUS_READY: + statusString = "READY"; + break; + case STATUS_IN_USE: + statusString = "IN_USE"; + break; + default: + statusString = "UNKNOWN"; + break; + } + + switch (cause) { + case CAUSE_INSTALL_COMPLETED: + causeString = "INSTALL_COMPLETED"; + break; + case CAUSE_INSTALL_CANCELLED: + causeString = "INSTALL_CANCELLED"; + break; + case CAUSE_ERROR_IO: + causeString = "ERROR_IO"; + break; + case CAUSE_ERROR_INVALID_URL: + causeString = "ERROR_INVALID_URL"; + break; + case CAUSE_ERROR_EXCEPTION: + causeString = "ERROR_EXCEPTION"; + break; + default: + causeString = "CAUSE_NOT_SPECIFIED"; + break; + } + + Log.d(TAG, "status=" + statusString + ", cause=" + causeString); boolean notifyOnNotificationBar = true; diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java index 440315ffe37b..2d7d59cc0022 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java @@ -118,7 +118,7 @@ public class WifiEntryPreference extends Preference implements WifiEntry.WifiEnt notifyChanged(); } - setSummary(mWifiEntry.getSummary()); + setSummary(mWifiEntry.getSummary(false /* concise */)); mContentDescription = buildContentDescription(); } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java index 752a5498a077..0f1e0ff60e37 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java @@ -67,7 +67,7 @@ public class WifiEntryPreferenceTest { MockitoAnnotations.initMocks(this); when(mMockWifiEntry.getTitle()).thenReturn(MOCK_TITLE); - when(mMockWifiEntry.getSummary()).thenReturn(MOCK_SUMMARY); + when(mMockWifiEntry.getSummary(false /* concise */)).thenReturn(MOCK_SUMMARY); when(mMockIconInjector.getIcon(0)).thenReturn(mMockDrawable0); when(mMockIconInjector.getIcon(1)).thenReturn(mMockDrawable1); @@ -112,7 +112,7 @@ public class WifiEntryPreferenceTest { final WifiEntryPreference pref = new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector); final String updatedSummary = "updated summary"; - when(mMockWifiEntry.getSummary()).thenReturn(updatedSummary); + when(mMockWifiEntry.getSummary(false /* concise */)).thenReturn(updatedSummary); pref.refresh(); diff --git a/packages/SystemUI/src/com/android/systemui/util/Assert.java b/packages/SystemUI/src/com/android/systemui/util/Assert.java index 096ac3fcee1d..f6e921e628ba 100644 --- a/packages/SystemUI/src/com/android/systemui/util/Assert.java +++ b/packages/SystemUI/src/com/android/systemui/util/Assert.java @@ -18,7 +18,7 @@ package com.android.systemui.util; import android.os.Looper; -import com.android.internal.annotations.VisibleForTesting; +import androidx.annotation.VisibleForTesting; /** * Helper providing common assertions. @@ -30,7 +30,9 @@ public class Assert { public static void isMainThread() { if (!sMainLooper.isCurrentThread()) { - throw new IllegalStateException("should be called from the main thread."); + throw new IllegalStateException("should be called from the main thread." + + " sMainLooper.threadName=" + sMainLooper.getThread().getName() + + " Thread.currentThread()=" + Thread.currentThread().getName()); } } diff --git a/packages/Tethering/jarjar-rules.txt b/packages/Tethering/jarjar-rules.txt index 3c182ec151ee..c6efa41e580a 100644 --- a/packages/Tethering/jarjar-rules.txt +++ b/packages/Tethering/jarjar-rules.txt @@ -16,4 +16,3 @@ rule com.android.internal.util.TrafficStatsConstants* com.android.networkstack.t rule android.net.LocalLog* com.android.networkstack.tethering.LocalLog@1 rule android.net.shared.Inet4AddressUtils* com.android.networkstack.tethering.shared.Inet4AddressUtils@1 -rule android.net.util.** com.android.networkstack.tethering.util.@1 diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java index 174836c14bd1..4dd68301f9fa 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java @@ -21,7 +21,8 @@ import android.net.LinkProperties; import android.net.NetworkCapabilities; import android.net.RouteInfo; import android.net.util.InterfaceSet; -import android.net.util.NetUtils; + +import com.android.net.module.util.NetUtils; import java.net.InetAddress; import java.net.UnknownHostException; diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 36a9e0ebbf5d..c9fdd5ae407a 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -1147,8 +1147,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub @ShortcutType int shortcutType) { Intent intent = new Intent(AccessibilityManager.ACTION_CHOOSE_ACCESSIBILITY_BUTTON); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + intent.putExtra(AccessibilityManager.EXTRA_SHORTCUT_TYPE, shortcutType); final Bundle bundle = ActivityOptions.makeBasic().setLaunchDisplayId(displayId).toBundle(); - bundle.putInt(AccessibilityManager.EXTRA_SHORTCUT_TYPE, shortcutType); mContext.startActivityAsUser(intent, bundle, UserHandle.of(mCurrentUserId)); } diff --git a/services/core/java/com/android/server/integrity/IntegrityFileManager.java b/services/core/java/com/android/server/integrity/IntegrityFileManager.java index d763baec945b..fffe7d9030ff 100644 --- a/services/core/java/com/android/server/integrity/IntegrityFileManager.java +++ b/services/core/java/com/android/server/integrity/IntegrityFileManager.java @@ -185,6 +185,10 @@ public class IntegrityFileManager { && tmpDir.renameTo(mStagingDir))) { throw new IOException("Error switching staging/rules directory"); } + + for (File file : mStagingDir.listFiles()) { + file.delete(); + } } } diff --git a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java index 0f8561e5afd9..4943c25b1f18 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java @@ -24,6 +24,7 @@ import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTE import android.app.ActivityManager; import android.app.admin.PasswordMetrics; import android.os.ShellCommand; +import android.text.TextUtils; import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.LockPatternUtils.RequestThrottledException; @@ -195,6 +196,9 @@ class LockSettingsShellCommand extends ShellCommand { } private LockscreenCredential getOldCredential() { + if (TextUtils.isEmpty(mOld)) { + return LockscreenCredential.createNone(); + } if (mLockPatternUtils.isLockPasswordEnabled(mCurrentUserId)) { final int quality = mLockPatternUtils.getKeyguardStoredPasswordQuality(mCurrentUserId); if (LockPatternUtils.isQualityAlphabeticPassword(quality)) { @@ -202,12 +206,15 @@ class LockSettingsShellCommand extends ShellCommand { } else { return LockscreenCredential.createPin(mOld); } - } else if (mLockPatternUtils.isLockPatternEnabled(mCurrentUserId)) { + } + if (mLockPatternUtils.isLockPatternEnabled(mCurrentUserId)) { return LockscreenCredential.createPattern(LockPatternUtils.byteArrayToPattern( mOld.getBytes())); - } else { - return LockscreenCredential.createNone(); } + // User supplied some old credential but the device has neither password nor pattern, + // so just return a password credential (and let it be rejected during LSS verification) + return LockscreenCredential.createPassword(mOld); + } private boolean runSetPattern() { diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java index eefcde6bd355..de48939825e4 100644 --- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java +++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java @@ -43,6 +43,7 @@ import android.content.rollback.RollbackManager; import android.os.Binder; import android.os.Environment; import android.os.Handler; +import android.os.HandlerExecutor; import android.os.HandlerThread; import android.os.Process; import android.os.SystemClock; @@ -78,6 +79,7 @@ import java.util.List; import java.util.Random; import java.util.Set; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executor; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; @@ -134,6 +136,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { private final Context mContext; private final HandlerThread mHandlerThread; + private final Executor mExecutor; private final Installer mInstaller; private final RollbackPackageHealthObserver mPackageHealthObserver; private final AppDataRollbackHelper mAppDataRollbackHelper; @@ -173,6 +176,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { mHandlerThread = new HandlerThread("RollbackManagerServiceHandler"); mHandlerThread.start(); Watchdog.getInstance().addThread(getHandler(), HANDLER_THREAD_TIMEOUT_DURATION_MILLIS); + mExecutor = new HandlerExecutor(getHandler()); for (UserInfo userInfo : UserManager.get(mContext).getUsers(true)) { registerUserCallbacks(userInfo.getUserHandle()); @@ -409,7 +413,6 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { CountDownLatch latch = new CountDownLatch(1); getHandler().post(() -> { - updateRollbackLifetimeDurationInMillis(); synchronized (mLock) { mRollbacks.clear(); mRollbacks.addAll(mRollbackStore.loadRollbacks()); @@ -520,11 +523,13 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { @AnyThread void onBootCompleted() { - getHandler().post(() -> updateRollbackLifetimeDurationInMillis()); - // Also posts to handler thread - scheduleExpiration(0); + DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_ROLLBACK_BOOT, + mExecutor, properties -> updateRollbackLifetimeDurationInMillis()); getHandler().post(() -> { + updateRollbackLifetimeDurationInMillis(); + runExpiration(); + // Check to see if any rollback-enabled staged sessions or staged // rollback sessions been applied. List<Rollback> enabling = new ArrayList<>(); diff --git a/services/core/java/com/android/server/stats/StatsPullAtomService.java b/services/core/java/com/android/server/stats/StatsPullAtomService.java index 75fbf01b1d3c..03bada4af842 100644 --- a/services/core/java/com/android/server/stats/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/StatsPullAtomService.java @@ -211,6 +211,8 @@ public class StatsPullAtomService extends SystemService { @Override public void onStart() { mStatsManager = (StatsManager) mContext.getSystemService(Context.STATS_MANAGER); + mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); + mTelephony = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); // Used to initialize the CPU Frequency atom. PowerProfile powerProfile = new PowerProfile(mContext); @@ -814,19 +816,96 @@ public class StatsPullAtomService extends SystemService { } private void registerWifiActivityInfo() { - // No op. + int tagId = StatsLog.WIFI_ACTIVITY_INFO; + mStatsManager.registerPullAtomCallback( + tagId, + null, // use default PullAtomMetadata values + (atomTag, data) -> pullWifiActivityInfo(atomTag, data), + BackgroundThread.getExecutor() + ); } - private void pullWifiActivityInfo() { - // No op. + private WifiManager mWifiManager; + private TelephonyManager mTelephony; + + private int pullWifiActivityInfo(int atomTag, List<StatsEvent> pulledData) { + long token = Binder.clearCallingIdentity(); + try { + SynchronousResultReceiver wifiReceiver = new SynchronousResultReceiver("wifi"); + mWifiManager.getWifiActivityEnergyInfoAsync( + new Executor() { + @Override + public void execute(Runnable runnable) { + // run the listener on the binder thread, if it was run on the main + // thread it would deadlock since we would be waiting on ourselves + runnable.run(); + } + }, + info -> { + Bundle bundle = new Bundle(); + bundle.putParcelable(BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY, info); + wifiReceiver.send(0, bundle); + } + ); + final WifiActivityEnergyInfo wifiInfo = awaitControllerInfo(wifiReceiver); + if (wifiInfo == null) { + return StatsManager.PULL_SKIP; + } + StatsEvent e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeLong(wifiInfo.getTimeSinceBootMillis()) + .writeInt(wifiInfo.getStackState()) + .writeLong(wifiInfo.getControllerTxDurationMillis()) + .writeLong(wifiInfo.getControllerRxDurationMillis()) + .writeLong(wifiInfo.getControllerIdleDurationMillis()) + .writeLong(wifiInfo.getControllerEnergyUsedMicroJoules()) + .build(); + pulledData.add(e); + } catch (RuntimeException e) { + Slog.e(TAG, "failed to getWifiActivityEnergyInfoAsync", e); + return StatsManager.PULL_SKIP; + } finally { + Binder.restoreCallingIdentity(token); + } + return StatsManager.PULL_SUCCESS; } private void registerModemActivityInfo() { - // No op. + int tagId = StatsLog.MODEM_ACTIVITY_INFO; + mStatsManager.registerPullAtomCallback( + tagId, + null, // use default PullAtomMetadata values + (atomTag, data) -> pullModemActivityInfo(atomTag, data), + BackgroundThread.getExecutor() + ); } - private void pullModemActivityInfo() { - // No op. + private int pullModemActivityInfo(int atomTag, List<StatsEvent> pulledData) { + long token = Binder.clearCallingIdentity(); + try { + SynchronousResultReceiver modemReceiver = new SynchronousResultReceiver("telephony"); + mTelephony.requestModemActivityInfo(modemReceiver); + final ModemActivityInfo modemInfo = awaitControllerInfo(modemReceiver); + if (modemInfo == null) { + return StatsManager.PULL_SKIP; + } + StatsEvent e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeLong(modemInfo.getTimestamp()) + .writeLong(modemInfo.getSleepTimeMillis()) + .writeLong(modemInfo.getIdleTimeMillis()) + .writeLong(modemInfo.getTransmitPowerInfo().get(0).getTimeInMillis()) + .writeLong(modemInfo.getTransmitPowerInfo().get(1).getTimeInMillis()) + .writeLong(modemInfo.getTransmitPowerInfo().get(2).getTimeInMillis()) + .writeLong(modemInfo.getTransmitPowerInfo().get(3).getTimeInMillis()) + .writeLong(modemInfo.getTransmitPowerInfo().get(4).getTimeInMillis()) + .writeLong(modemInfo.getReceiveTimeMillis()) + .build(); + pulledData.add(e); + } finally { + Binder.restoreCallingIdentity(token); + } + return StatsManager.PULL_SUCCESS; } private void registerBluetoothActivityInfo() { diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 19bc560dd838..3f3408f4d202 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -6363,6 +6363,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A * aspect ratio. */ boolean shouldUseSizeCompatMode() { + if (inMultiWindowMode() || getWindowConfiguration().hasWindowDecorCaption()) { + final ActivityRecord root = task != null ? task.getRootActivity() : null; + if (root != null && root != this && !root.shouldUseSizeCompatMode()) { + // If the root activity doesn't use size compatibility mode, the activities above + // are forced to be the same for consistent visual appearance. + return false; + } + } return !isResizeable() && (info.isFixedOrientation() || info.hasFixedAspectRatio()) // The configuration of non-standard type should be enforced by system. && isActivityTypeStandard() diff --git a/services/core/jni/com_android_server_GraphicsStatsService.cpp b/services/core/jni/com_android_server_GraphicsStatsService.cpp index 9353fbd71214..7644adebb10a 100644 --- a/services/core/jni/com_android_server_GraphicsStatsService.cpp +++ b/services/core/jni/com_android_server_GraphicsStatsService.cpp @@ -178,15 +178,16 @@ static void writeGpuHistogram(stats_event* event, } // graphicsStatsPullCallback is invoked by statsd service to pull GRAPHICS_STATS atom. -static bool graphicsStatsPullCallback(int32_t atom_tag, pulled_stats_event_list* data, - const void* cookie) { +static status_pull_atom_return_t graphicsStatsPullCallback(int32_t atom_tag, + pulled_stats_event_list* data, + void* cookie) { JNIEnv* env = getJNIEnv(); if (!env) { return false; } if (gGraphicsStatsServiceObject == nullptr) { ALOGE("Failed to get graphicsstats service"); - return false; + return STATS_PULL_SKIP; } for (bool lastFullDay : {true, false}) { @@ -198,7 +199,7 @@ static bool graphicsStatsPullCallback(int32_t atom_tag, pulled_stats_event_list* env->ExceptionDescribe(); env->ExceptionClear(); ALOGE("Failed to invoke graphicsstats service"); - return false; + return STATS_PULL_SKIP; } if (!jdata) { // null means data is not available for that day. @@ -217,7 +218,7 @@ static bool graphicsStatsPullCallback(int32_t atom_tag, pulled_stats_event_list* if (!success) { ALOGW("Parse failed on GraphicsStatsPuller error='%s' dataSize='%d'", serviceDump.InitializationErrorString().c_str(), dataSize); - return false; + return STATS_PULL_SKIP; } for (int stat_index = 0; stat_index < serviceDump.stats_size(); stat_index++) { @@ -244,7 +245,7 @@ static bool graphicsStatsPullCallback(int32_t atom_tag, pulled_stats_event_list* stats_event_build(event); } } - return true; + return STATS_PULL_SUCCESS; } // Register a puller for GRAPHICS_STATS atom with the statsd service. diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index c55acab223d9..b8b0dbf9157f 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -1095,7 +1095,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { String globalProxySpec = null; String globalProxyExclusionList = null; - ArrayMap<String, TrustAgentInfo> trustAgentInfos = new ArrayMap<>(); + @NonNull ArrayMap<String, TrustAgentInfo> trustAgentInfos = new ArrayMap<>(); List<String> crossProfileWidgetProviders; @@ -1650,6 +1650,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } + @NonNull private ArrayMap<String, TrustAgentInfo> getAllTrustAgentInfos( XmlPullParser parser, String tag) throws XmlPullParserException, IOException { int outerDepthDAM = parser.getDepth(); @@ -2434,11 +2435,133 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { migrateUserRestrictionsIfNecessaryLocked(); // TODO PO may not have a class name either due to b/17652534. Address that too. - updateDeviceOwnerLocked(); } } + /** + * Checks if the device is in COMP mode, and if so migrates it to managed profile on a + * corporate owned device. + */ + @GuardedBy("getLockObject()") + private void maybeMigrateToProfileOnOrganizationOwnedDeviceLocked() { + logIfVerbose("Checking whether we need to migrate COMP "); + final int doUserId = mOwners.getDeviceOwnerUserId(); + if (doUserId == UserHandle.USER_NULL) { + logIfVerbose("No DO found, skipping migration."); + return; + } + + final List<UserInfo> profiles = mUserManager.getProfiles(doUserId); + if (profiles.size() != 2) { + if (profiles.size() == 1) { + logIfVerbose("Profile not found, skipping migration."); + } else { + Slog.wtf(LOG_TAG, "Found " + profiles.size() + " profiles, skipping migration"); + } + return; + } + + final int poUserId = getManagedUserId(doUserId); + if (poUserId < 0) { + Slog.wtf(LOG_TAG, "Found DO and a profile, but it is not managed, skipping migration"); + return; + } + + final ActiveAdmin doAdmin = getDeviceOwnerAdminLocked(); + final ActiveAdmin poAdmin = getProfileOwnerAdminLocked(poUserId); + if (doAdmin == null || poAdmin == null) { + Slog.wtf(LOG_TAG, "Failed to get either PO or DO admin, aborting migration."); + return; + } + + final ComponentName doAdminComponent = mOwners.getDeviceOwnerComponent(); + final ComponentName poAdminComponent = mOwners.getProfileOwnerComponent(poUserId); + if (doAdminComponent == null || poAdminComponent == null) { + Slog.wtf(LOG_TAG, "Cannot find PO or DO component name, aborting migration."); + return; + } + if (!doAdminComponent.getPackageName().equals(poAdminComponent.getPackageName())) { + Slog.e(LOG_TAG, "DO and PO are different packages, aborting migration."); + return; + } + + Slog.i(LOG_TAG, String.format( + "Migrating COMP to PO on a corp owned device; primary user: %d; profile: %d", + doUserId, poUserId)); + + Slog.i(LOG_TAG, "Giving the PO additional power..."); + markProfileOwnerOnOrganizationOwnedDeviceUncheckedLocked(poAdminComponent, poUserId); + Slog.i(LOG_TAG, "Migrating DO policies to PO..."); + moveDoPoliciesToProfileParentAdmin(doAdmin, poAdmin.getParentActiveAdmin()); + saveSettingsLocked(poUserId); + Slog.i(LOG_TAG, "Clearing the DO..."); + final ComponentName doAdminReceiver = doAdmin.info.getComponent(); + clearDeviceOwnerLocked(doAdmin, doUserId); + // TODO(b/143516163): If we have a power cut here, we might leave active admin. Consider if + // it is worth the complexity to make it more robust. + Slog.i(LOG_TAG, "Removing admin artifacts..."); + // TODO(b/143516163): Clean up application restrictions in UserManager. + removeAdminArtifacts(doAdminReceiver, doUserId); + Slog.i(LOG_TAG, "Migration complete."); + + // Note: KeyChain keys are not removed and will remain accessible for the apps that have + // been given grants to use them. + } + + private void moveDoPoliciesToProfileParentAdmin(ActiveAdmin doAdmin, ActiveAdmin parentAdmin) { + // The following policies can be already controlled via parent instance, skip if so. + if (parentAdmin.mPasswordPolicy.quality == PASSWORD_QUALITY_UNSPECIFIED) { + parentAdmin.mPasswordPolicy = doAdmin.mPasswordPolicy; + } + if (parentAdmin.passwordHistoryLength == ActiveAdmin.DEF_PASSWORD_HISTORY_LENGTH) { + parentAdmin.passwordHistoryLength = doAdmin.passwordHistoryLength; + } + if (parentAdmin.passwordExpirationTimeout == ActiveAdmin.DEF_PASSWORD_HISTORY_LENGTH) { + parentAdmin.passwordExpirationTimeout = doAdmin.passwordExpirationTimeout; + } + if (parentAdmin.maximumFailedPasswordsForWipe + == ActiveAdmin.DEF_MAXIMUM_FAILED_PASSWORDS_FOR_WIPE) { + parentAdmin.maximumFailedPasswordsForWipe = doAdmin.maximumFailedPasswordsForWipe; + } + if (parentAdmin.maximumTimeToUnlock == ActiveAdmin.DEF_MAXIMUM_TIME_TO_UNLOCK) { + parentAdmin.maximumTimeToUnlock = doAdmin.maximumTimeToUnlock; + } + if (parentAdmin.strongAuthUnlockTimeout + == DevicePolicyManager.DEFAULT_STRONG_AUTH_TIMEOUT_MS) { + parentAdmin.strongAuthUnlockTimeout = doAdmin.strongAuthUnlockTimeout; + } + parentAdmin.disabledKeyguardFeatures |= + doAdmin.disabledKeyguardFeatures & PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER; + + parentAdmin.trustAgentInfos.putAll(doAdmin.trustAgentInfos); + + // The following policies weren't available to PO, but will be available after migration. + parentAdmin.disableCamera = doAdmin.disableCamera; + + // TODO(b/143516163): Uncomment once corresponding APIs are available via parent instance. + // parentAdmin.disableScreenCapture = doAdmin.disableScreenCapture; + // parentAdmin.accountTypesWithManagementDisabled.addAll( + // doAdmin.accountTypesWithManagementDisabled); + + moveDoUserRestrictionsToCopeParent(doAdmin, parentAdmin); + + // TODO(b/143516163): migrate network and security logging state, currently they are + // turned off when DO is removed. + } + + private void moveDoUserRestrictionsToCopeParent(ActiveAdmin doAdmin, ActiveAdmin parentAdmin) { + if (doAdmin.userRestrictions == null) { + return; + } + for (final String restriction : doAdmin.userRestrictions.keySet()) { + if (UserRestrictionsUtils.canProfileOwnerOfOrganizationOwnedDeviceChange(restriction)) { + parentAdmin.userRestrictions.putBoolean( + restriction, doAdmin.userRestrictions.getBoolean(restriction)); + } + } + } + /** Apply default restrictions that haven't been applied to profile owners yet. */ private void maybeSetDefaultProfileOwnerUserRestrictions() { synchronized (getLockObject()) { @@ -3625,6 +3748,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { break; case SystemService.PHASE_ACTIVITY_MANAGER_READY: maybeStartSecurityLogMonitorOnActivityManagerReady(); + synchronized (getLockObject()) { + maybeMigrateToProfileOnOrganizationOwnedDeviceLocked(); + } break; case SystemService.PHASE_BOOT_COMPLETED: ensureDeviceOwnerUserStarted(); // TODO Consider better place to do this. @@ -12919,37 +13045,43 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // Grant access under lock. synchronized (getLockObject()) { - // Sanity check: Make sure that the user has a profile owner and that the specified - // component is the profile owner of that user. - if (!isProfileOwner(who, userId)) { - throw new IllegalArgumentException(String.format( - "Component %s is not a Profile Owner of user %d", - who.flattenToString(), userId)); - } + markProfileOwnerOnOrganizationOwnedDeviceUncheckedLocked(who, userId); + } + } - Slog.i(LOG_TAG, String.format( - "Marking %s as profile owner on organization-owned device for user %d", + @GuardedBy("getLockObject()") + private void markProfileOwnerOnOrganizationOwnedDeviceUncheckedLocked( + ComponentName who, int userId) { + // Sanity check: Make sure that the user has a profile owner and that the specified + // component is the profile owner of that user. + if (!isProfileOwner(who, userId)) { + throw new IllegalArgumentException(String.format( + "Component %s is not a Profile Owner of user %d", who.flattenToString(), userId)); + } - // First, set restriction on removing the profile. - mInjector.binderWithCleanCallingIdentity(() -> { - // Clear restriction as user. - UserHandle parentUser = mUserManager.getProfileParent(UserHandle.of(userId)); - if (!parentUser.isSystem()) { - throw new IllegalStateException( - String.format("Only the profile owner of a managed profile on the" + Slog.i(LOG_TAG, String.format( + "Marking %s as profile owner on organization-owned device for user %d", + who.flattenToString(), userId)); + + // First, set restriction on removing the profile. + mInjector.binderWithCleanCallingIdentity(() -> { + // Clear restriction as user. + final UserHandle parentUser = mUserManager.getProfileParent(UserHandle.of(userId)); + if (!parentUser.isSystem()) { + throw new IllegalStateException( + String.format("Only the profile owner of a managed profile on the" + " primary user can be granted access to device identifiers, not" + " on user %d", parentUser.getIdentifier())); - } + } - mUserManager.setUserRestriction(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE, true, - parentUser); - }); + mUserManager.setUserRestriction(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE, true, + parentUser); + }); - // markProfileOwnerOfOrganizationOwnedDevice will trigger writing of the profile owner - // data, no need to do it manually. - mOwners.markProfileOwnerOfOrganizationOwnedDevice(userId); - } + // markProfileOwnerOfOrganizationOwnedDevice will trigger writing of the profile owner + // data, no need to do it manually. + mOwners.markProfileOwnerOfOrganizationOwnedDevice(userId); } private void pushMeteredDisabledPackagesLocked(int userId) { @@ -14913,4 +15045,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return packages == null ? Collections.EMPTY_LIST : packages; } } + + private void logIfVerbose(String message) { + if (VERBOSE_LOG) { + Slog.d(LOG_TAG, message); + } + } } diff --git a/services/tests/servicestests/res/raw/comp_device_owner.xml b/services/tests/servicestests/res/raw/comp_device_owner.xml new file mode 100644 index 000000000000..0a10242ec59d --- /dev/null +++ b/services/tests/servicestests/res/raw/comp_device_owner.xml @@ -0,0 +1,8 @@ +<?xml version='1.0' encoding='utf-8' standalone='yes' ?> +<root> + <device-owner package="com.android.frameworks.servicestests" + name="" + component="com.android.frameworks.servicestests/com.android.server.devicepolicy.DummyDeviceAdmins$Admin1" + userRestrictionsMigrated="true" /> + <device-owner-context userId="0" /> +</root> diff --git a/services/tests/servicestests/res/raw/comp_policies_primary.xml b/services/tests/servicestests/res/raw/comp_policies_primary.xml new file mode 100644 index 000000000000..1e1a0eff874c --- /dev/null +++ b/services/tests/servicestests/res/raw/comp_policies_primary.xml @@ -0,0 +1,7 @@ +<?xml version='1.0' encoding='utf-8' standalone='yes' ?> +<policies setup-complete="true" provisioning-state="3"> + <admin name="com.android.frameworks.servicestests/com.android.server.devicepolicy.DummyDeviceAdmins$Admin1"> + <policies flags="991"/> + <password-history-length value="33" /> + </admin> +</policies> diff --git a/services/tests/servicestests/res/raw/comp_policies_profile_another_package.xml b/services/tests/servicestests/res/raw/comp_policies_profile_another_package.xml new file mode 100644 index 000000000000..141315e6c2d2 --- /dev/null +++ b/services/tests/servicestests/res/raw/comp_policies_profile_another_package.xml @@ -0,0 +1,6 @@ +<?xml version='1.0' encoding='utf-8' standalone='yes' ?> +<policies setup-complete="true" provisioning-state="3"> + <admin name="com.another.package.name/whatever.random.class"> + <policies flags="991"/> + </admin> +</policies> diff --git a/services/tests/servicestests/res/raw/comp_policies_profile_same_package.xml b/services/tests/servicestests/res/raw/comp_policies_profile_same_package.xml new file mode 100644 index 000000000000..c874dcca2c73 --- /dev/null +++ b/services/tests/servicestests/res/raw/comp_policies_profile_same_package.xml @@ -0,0 +1,6 @@ +<?xml version='1.0' encoding='utf-8' standalone='yes' ?> +<policies setup-complete="true" provisioning-state="3"> + <admin name="com.android.frameworks.servicestests/com.android.server.devicepolicy.DummyDeviceAdmins$Admin1"> + <policies flags="991"/> + </admin> +</policies> diff --git a/services/tests/servicestests/res/raw/comp_profile_owner_another_package.xml b/services/tests/servicestests/res/raw/comp_profile_owner_another_package.xml new file mode 100644 index 000000000000..d65ba7826fef --- /dev/null +++ b/services/tests/servicestests/res/raw/comp_profile_owner_another_package.xml @@ -0,0 +1,7 @@ +<?xml version='1.0' encoding='utf-8' standalone='yes' ?> +<root> + <profile-owner package="com.another.package.name" + name="com.another.package.name" + component="com.another.package.name/whatever.random.class" + userRestrictionsMigrated="true"/> +</root> diff --git a/services/tests/servicestests/res/raw/comp_profile_owner_same_package.xml b/services/tests/servicestests/res/raw/comp_profile_owner_same_package.xml new file mode 100644 index 000000000000..7f98c91c0a94 --- /dev/null +++ b/services/tests/servicestests/res/raw/comp_profile_owner_same_package.xml @@ -0,0 +1,7 @@ +<?xml version='1.0' encoding='utf-8' standalone='yes' ?> +<root> + <profile-owner package="com.android.frameworks.servicestests" + name="com.android.frameworks.servicestests" + component="com.android.frameworks.servicestests/com.android.server.devicepolicy.DummyDeviceAdmins$Admin1" + userRestrictionsMigrated="true"/> +</root> diff --git a/services/tests/servicestests/src/com/android/server/appsearch/impl/AppSearchImplTest.java b/services/tests/servicestests/src/com/android/server/appsearch/impl/AppSearchImplTest.java new file mode 100644 index 000000000000..41956794aaf2 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/appsearch/impl/AppSearchImplTest.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2019 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.appsearch.impl; + +import static com.google.common.truth.Truth.assertThat; + +import static org.testng.Assert.expectThrows; + +import android.annotation.UserIdInt; +import android.content.Context; +import android.os.UserHandle; + +import androidx.test.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + +import com.google.android.icing.proto.IndexingConfig; +import com.google.android.icing.proto.PropertyConfigProto; +import com.google.android.icing.proto.SchemaProto; +import com.google.android.icing.proto.SchemaTypeConfigProto; +import com.google.android.icing.proto.TermMatchType; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class AppSearchImplTest { + private final Context mContext = InstrumentationRegistry.getContext(); + private final @UserIdInt int mUserId = UserHandle.getCallingUserId(); + + @Test + public void testRewriteSchemaTypes() { + SchemaProto inSchema = SchemaProto.newBuilder() + .addTypes(SchemaTypeConfigProto.newBuilder() + .setSchemaType("TestType") + .addProperties(PropertyConfigProto.newBuilder() + .setPropertyName("subject") + .setDataType(PropertyConfigProto.DataType.Code.STRING) + .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL) + .setIndexingConfig( + IndexingConfig.newBuilder() + .setTokenizerType( + IndexingConfig.TokenizerType.Code.PLAIN) + .setTermMatchType(TermMatchType.Code.PREFIX) + .build() + ).build() + ).addProperties(PropertyConfigProto.newBuilder() + .setPropertyName("link") + .setDataType(PropertyConfigProto.DataType.Code.DOCUMENT) + .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL) + .setSchemaType("RefType") + .build() + ).build() + ).build(); + + SchemaProto expectedSchema = SchemaProto.newBuilder() + .addTypes(SchemaTypeConfigProto.newBuilder() + .setSchemaType("com.android.server.appsearch.impl@42:TestType") + .addProperties(PropertyConfigProto.newBuilder() + .setPropertyName("subject") + .setDataType(PropertyConfigProto.DataType.Code.STRING) + .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL) + .setIndexingConfig( + IndexingConfig.newBuilder() + .setTokenizerType( + IndexingConfig.TokenizerType.Code.PLAIN) + .setTermMatchType(TermMatchType.Code.PREFIX) + .build() + ).build() + ).addProperties(PropertyConfigProto.newBuilder() + .setPropertyName("link") + .setDataType(PropertyConfigProto.DataType.Code.DOCUMENT) + .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL) + .setSchemaType("com.android.server.appsearch.impl@42:RefType") + .build() + ).build() + ).build(); + + AppSearchImpl impl = new AppSearchImpl(mContext, mUserId); + SchemaProto.Builder actualSchema = inSchema.toBuilder(); + impl.rewriteSchemaTypes("com.android.server.appsearch.impl@42:", actualSchema); + + assertThat(actualSchema.build()).isEqualTo(expectedSchema); + } + + @Test + public void testPackageNotFound() { + AppSearchImpl impl = new AppSearchImpl(mContext, mUserId); + IllegalStateException e = expectThrows( + IllegalStateException.class, + () -> impl.setSchema( + /*callingUid=*/Integer.MAX_VALUE, SchemaProto.getDefaultInstance())); + assertThat(e).hasMessageThat().contains("Failed to look up package name"); + } +} diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java index 5f1f3083361b..46b83713c159 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java @@ -15,6 +15,10 @@ */ package com.android.server.devicepolicy; +import static android.os.UserHandle.USER_SYSTEM; + +import static com.android.server.devicepolicy.DpmTestUtils.writeInputStreamToFile; + import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.eq; @@ -22,12 +26,14 @@ import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.when; import android.app.admin.DevicePolicyManagerInternal; +import android.content.ComponentName; import android.content.pm.PackageManager; import android.os.Bundle; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; +import com.android.frameworks.servicestests.R; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.devicepolicy.DevicePolicyManagerServiceTestable.OwnersTestable; @@ -37,9 +43,13 @@ import java.util.HashMap; import java.util.Map; import java.util.Set; +// TODO (b/143516163): Fix old test cases and put into presubmit. public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase { private static final String USER_TYPE_EMPTY = ""; + private static final int COPE_ADMIN1_APP_ID = 123; + private static final int COPE_ANOTHER_ADMIN_APP_ID = 125; + private static final int COPE_PROFILE_USER_ID = 11; private DpmMockContext mContext; @@ -85,7 +95,7 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase { // Set up UserManager when(getServices().userManagerInternal.getBaseUserRestrictions( - eq(UserHandle.USER_SYSTEM))).thenReturn(DpmTestUtils.newRestrictions( + eq(USER_SYSTEM))).thenReturn(DpmTestUtils.newRestrictions( UserManager.DISALLOW_ADD_USER, UserManager.DISALLOW_RECORD_AUDIO)); @@ -137,7 +147,7 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase { } assertTrue(dpms.mOwners.hasDeviceOwner()); - assertFalse(dpms.mOwners.hasProfileOwner(UserHandle.USER_SYSTEM)); + assertFalse(dpms.mOwners.hasProfileOwner(USER_SYSTEM)); assertTrue(dpms.mOwners.hasProfileOwner(10)); assertTrue(dpms.mOwners.hasProfileOwner(11)); assertFalse(dpms.mOwners.hasProfileOwner(12)); @@ -145,7 +155,7 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase { // Now all information should be migrated. assertFalse(dpms.mOwners.getDeviceOwnerUserRestrictionsNeedsMigration()); assertFalse(dpms.mOwners.getProfileOwnerUserRestrictionsNeedsMigration( - UserHandle.USER_SYSTEM)); + USER_SYSTEM)); assertFalse(dpms.mOwners.getProfileOwnerUserRestrictionsNeedsMigration(10)); assertFalse(dpms.mOwners.getProfileOwnerUserRestrictionsNeedsMigration(11)); assertFalse(dpms.mOwners.getProfileOwnerUserRestrictionsNeedsMigration(12)); @@ -155,7 +165,7 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase { DpmTestUtils.newRestrictions( UserManager.DISALLOW_RECORD_AUDIO ), - newBaseRestrictions.get(UserHandle.USER_SYSTEM)); + newBaseRestrictions.get(USER_SYSTEM)); DpmTestUtils.assertRestrictions( DpmTestUtils.newRestrictions( @@ -214,7 +224,7 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase { // Set up UserManager when(getServices().userManagerInternal.getBaseUserRestrictions( - eq(UserHandle.USER_SYSTEM))).thenReturn(DpmTestUtils.newRestrictions( + eq(USER_SYSTEM))).thenReturn(DpmTestUtils.newRestrictions( UserManager.DISALLOW_ADD_USER, UserManager.DISALLOW_RECORD_AUDIO, UserManager.DISALLOW_SMS, @@ -249,19 +259,19 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase { mContext.binder.restoreCallingIdentity(ident); } assertFalse(dpms.mOwners.hasDeviceOwner()); - assertTrue(dpms.mOwners.hasProfileOwner(UserHandle.USER_SYSTEM)); + assertTrue(dpms.mOwners.hasProfileOwner(USER_SYSTEM)); // Now all information should be migrated. assertFalse(dpms.mOwners.getDeviceOwnerUserRestrictionsNeedsMigration()); assertFalse(dpms.mOwners.getProfileOwnerUserRestrictionsNeedsMigration( - UserHandle.USER_SYSTEM)); + USER_SYSTEM)); // Check the new base restrictions. DpmTestUtils.assertRestrictions( DpmTestUtils.newRestrictions( UserManager.DISALLOW_RECORD_AUDIO ), - newBaseRestrictions.get(UserHandle.USER_SYSTEM)); + newBaseRestrictions.get(USER_SYSTEM)); // Check the new owner restrictions. DpmTestUtils.assertRestrictions( @@ -270,7 +280,7 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase { UserManager.DISALLOW_SMS, UserManager.DISALLOW_OUTGOING_CALLS ), - dpms.getProfileOwnerAdminLocked(UserHandle.USER_SYSTEM).ensureUserRestrictions()); + dpms.getProfileOwnerAdminLocked(USER_SYSTEM).ensureUserRestrictions()); } // Test setting default restrictions for managed profile. @@ -332,4 +342,92 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase { assertEquals(alreadySet.size(), 1); assertTrue(alreadySet.contains(UserManager.DISALLOW_BLUETOOTH_SHARING)); } + + public void testCompMigrationUnAffiliated_skipped() throws Exception { + prepareAdmin1AsDo(); + prepareAdminAnotherPackageAsPo(COPE_PROFILE_USER_ID); + + final DevicePolicyManagerServiceTestable dpms; + dpms = bootDpmsUp(); + + // DO should still be DO since no migration should happen. + assertTrue(dpms.mOwners.hasDeviceOwner()); + } + + public void testCompMigrationAffiliated() throws Exception { + prepareAdmin1AsDo(); + prepareAdmin1AsPo(COPE_PROFILE_USER_ID); + + // Secure lock screen is needed for password policy APIs to work. + when(getServices().lockPatternUtils.hasSecureLockScreen()).thenReturn(true); + + final DevicePolicyManagerServiceTestable dpms; + dpms = bootDpmsUp(); + + // DO should cease to be DO. + assertFalse(dpms.mOwners.hasDeviceOwner()); + + final DpmMockContext poContext = new DpmMockContext(getServices(), mRealTestContext); + poContext.binder.callingUid = UserHandle.getUid(COPE_PROFILE_USER_ID, COPE_ADMIN1_APP_ID); + + runAsCaller(poContext, dpms, dpm -> { + // Check that DO policy is now set on parent instance. + assertEquals(33, dpm.getParentProfileInstance(admin1).getPasswordHistoryLength(admin1)); + // And NOT set on profile instance. + assertEquals(0, dpm.getPasswordHistoryLength(admin1)); + + // TODO(b/143516163): verify more policies. + }); + } + + private DevicePolicyManagerServiceTestable bootDpmsUp() { + DevicePolicyManagerServiceTestable dpms; + final long ident = mContext.binder.clearCallingIdentity(); + try { + LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class); + + dpms = new DevicePolicyManagerServiceTestable(getServices(), mContext); + + dpms.systemReady(SystemService.PHASE_LOCK_SETTINGS_READY); + dpms.systemReady(SystemService.PHASE_ACTIVITY_MANAGER_READY); + dpms.systemReady(SystemService.PHASE_BOOT_COMPLETED); + } finally { + mContext.binder.restoreCallingIdentity(ident); + } + return dpms; + } + + private void prepareAdmin1AsDo() throws Exception { + setUpPackageManagerForAdmin(admin1, UserHandle.getUid(USER_SYSTEM, COPE_ADMIN1_APP_ID)); + final int xmlResource = R.raw.comp_policies_primary; + writeInputStreamToFile(getRawStream(xmlResource), + (new File(getServices().systemUserDataDir, "device_policies.xml")) + .getAbsoluteFile()); + writeInputStreamToFile(getRawStream(R.raw.comp_device_owner), + (new File(getServices().dataDir, "device_owner_2.xml")) + .getAbsoluteFile()); + } + + private void prepareAdmin1AsPo(int profileUserId) throws Exception { + preparePo(profileUserId, admin1, R.raw.comp_profile_owner_same_package, + R.raw.comp_policies_profile_same_package, COPE_ADMIN1_APP_ID); + } + + private void prepareAdminAnotherPackageAsPo(int profileUserId) throws Exception { + preparePo(profileUserId, adminAnotherPackage, R.raw.comp_profile_owner_another_package, + R.raw.comp_policies_profile_another_package, COPE_ANOTHER_ADMIN_APP_ID); + } + + private void preparePo(int profileUserId, ComponentName admin, int profileOwnerXmlResId, + int policyXmlResId, int adminAppId) throws Exception { + final File profileDir = getServices().addUser(profileUserId, 0, + UserManager.USER_TYPE_PROFILE_MANAGED, USER_SYSTEM /* profile group */); + setUpPackageManagerForFakeAdmin( + admin, UserHandle.getUid(profileUserId, adminAppId), admin1); + writeInputStreamToFile(getRawStream(policyXmlResId), + (new File(profileDir, "device_policies.xml")).getAbsoluteFile()); + writeInputStreamToFile(getRawStream(profileOwnerXmlResId), + (new File(profileDir, "profile_owner.xml")).getAbsoluteFile()); + } + } diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index 2cb71039199a..bfadeea40034 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -58,7 +58,6 @@ import static org.mockito.hamcrest.MockitoHamcrest.argThat; import static org.testng.Assert.assertThrows; import android.Manifest.permission; -import android.annotation.RawRes; import android.app.Activity; import android.app.AppOpsManager; import android.app.Notification; @@ -112,7 +111,6 @@ import org.mockito.internal.util.collections.Sets; import org.mockito.stubbing.Answer; import java.io.File; -import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -5810,10 +5808,6 @@ public class DevicePolicyManagerTest extends DpmTestBase { return new File(parentDir, "device_policies.xml"); } - private InputStream getRawStream(@RawRes int id) { - return mRealTestContext.getResources().openRawResource(id); - } - private void setUserSetupCompleteForUser(boolean isUserSetupComplete, int userhandle) { when(getServices().settings.settingsSecureGetIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 0, userhandle)).thenReturn(isUserSetupComplete ? 1 : 0); diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java index a34c2ff8ce07..9a1a5fbfd186 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java @@ -24,6 +24,7 @@ import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.when; +import android.annotation.RawRes; import android.app.admin.DevicePolicyManager; import android.content.ComponentName; import android.content.Context; @@ -36,6 +37,7 @@ import android.content.pm.ResolveInfo; import android.os.UserHandle; import android.test.AndroidTestCase; +import java.io.InputStream; import java.util.List; public abstract class DpmTestBase extends AndroidTestCase { @@ -256,4 +258,8 @@ public abstract class DpmTestBase extends AndroidTestCase { invocation -> invocation.getArguments()[1] ); } + + protected InputStream getRawStream(@RawRes int id) { + return mRealTestContext.getResources().openRawResource(id); + } } diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java index 6c2c1446a459..068daf5ee310 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java @@ -15,6 +15,7 @@ */ package com.android.server.devicepolicy; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; @@ -236,6 +237,13 @@ public class MockSystemServices { return ui == null ? null : getUserInfo(ui.profileGroupId); } ); + when(userManager.getProfileParent(any(UserHandle.class))).thenAnswer( + invocation -> { + final UserHandle userHandle = (UserHandle) invocation.getArguments()[0]; + final UserInfo ui = getUserInfo(userHandle.getIdentifier()); + return ui == null ? UserHandle.USER_NULL : UserHandle.of(ui.profileGroupId); + } + ); when(userManager.getProfiles(anyInt())).thenAnswer( invocation -> { final int userId12 = (int) invocation.getArguments()[0]; diff --git a/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java b/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java index 5aed194773f5..47c7e56febe0 100644 --- a/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java @@ -135,14 +135,15 @@ public class IntegrityFileManagerTest { Arrays.asList(packageNameRule, packageCertRule, versionCodeRule, randomRule); mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, rules); - AppInstallMetadata appInstallMetadata = new AppInstallMetadata.Builder() - .setPackageName(packageName) - .setAppCertificate(packageCert) - .setVersionCode(version) - .setInstallerName("abc") - .setInstallerCertificate("abc") - .setIsPreInstalled(true) - .build(); + AppInstallMetadata appInstallMetadata = + new AppInstallMetadata.Builder() + .setPackageName(packageName) + .setAppCertificate(packageCert) + .setVersionCode(version) + .setInstallerName("abc") + .setInstallerCertificate("abc") + .setIsPreInstalled(true) + .build(); List<Rule> rulesFetched = mIntegrityFileManager.readRules(appInstallMetadata); assertThat(rulesFetched) @@ -174,14 +175,15 @@ public class IntegrityFileManagerTest { // Read the rules for a specific rule. String installedPackageName = String.format("%s%04d", packageName, 264); String installedAppCertificate = String.format("%s%04d", appCertificate, 1264); - AppInstallMetadata appInstallMetadata = new AppInstallMetadata.Builder() - .setPackageName(installedPackageName) - .setAppCertificate(installedAppCertificate) - .setVersionCode(250) - .setInstallerName("abc") - .setInstallerCertificate("abc") - .setIsPreInstalled(true) - .build(); + AppInstallMetadata appInstallMetadata = + new AppInstallMetadata.Builder() + .setPackageName(installedPackageName) + .setAppCertificate(installedAppCertificate) + .setVersionCode(250) + .setInstallerName("abc") + .setInstallerCertificate("abc") + .setIsPreInstalled(true) + .build(); List<Rule> rulesFetched = mIntegrityFileManager.readRules(appInstallMetadata); // Verify that we do not load all the rules and we have the necessary rules to evaluate. @@ -195,27 +197,38 @@ public class IntegrityFileManagerTest { private Rule getPackageNameIndexedRule(String packageName) { return new Rule( new StringAtomicFormula( - AtomicFormula.PACKAGE_NAME, - packageName, - /* isHashedValue= */ false), + AtomicFormula.PACKAGE_NAME, packageName, /* isHashedValue= */ false), Rule.DENY); } private Rule getAppCertificateIndexedRule(String appCertificate) { return new Rule( new StringAtomicFormula( - AtomicFormula.APP_CERTIFICATE, - appCertificate, - /* isHashedValue= */ false), + AtomicFormula.APP_CERTIFICATE, appCertificate, /* isHashedValue= */ false), Rule.DENY); } private Rule getInstallerCertificateRule(String installerCert) { return new Rule( new StringAtomicFormula( - AtomicFormula.INSTALLER_NAME, - installerCert, - /* isHashedValue= */ false), + AtomicFormula.INSTALLER_NAME, installerCert, /* isHashedValue= */ false), Rule.DENY); } + + @Test + public void testStagingDirectoryCleared() throws Exception { + // We must push rules two times to ensure that staging directory is empty because we cleared + // it, rather than because original rules directory is empty. + mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, Collections.EMPTY_LIST); + mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, Collections.EMPTY_LIST); + + assertStagingDirectoryCleared(); + } + + private void assertStagingDirectoryCleared() { + File stagingDir = new File(mTmpDir, "integrity_staging"); + assertThat(stagingDir.exists()).isTrue(); + assertThat(stagingDir.isDirectory()).isTrue(); + assertThat(stagingDir.listFiles()).isEmpty(); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java index 64db89706d39..890e4ab90ce9 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -391,6 +391,33 @@ public class SizeCompatTests extends ActivityTestsBase { assertEquals(null, compatTokens.get(0)); } + @Test + public void testShouldUseSizeCompatModeOnResizableTask() { + setUpApp(new TestDisplayContent.Builder(mService, 1000, 2500).build()); + + // Make the task root resizable. + mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_RESIZEABLE; + + // Create a size compat activity on the same task. + final ActivityRecord activity = new ActivityBuilder(mService) + .setTask(mTask) + .setResizeMode(ActivityInfo.RESIZE_MODE_UNRESIZEABLE) + .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) + .build(); + assertTrue(activity.shouldUseSizeCompatMode()); + + // The non-resizable activity should not be size compat because it is on a resizable task + // in multi-window mode. + mStack.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM); + assertFalse(activity.shouldUseSizeCompatMode()); + + // The non-resizable activity should not be size compat because the display support + // changing windowing mode from fullscreen to freeform. + mStack.mDisplayContent.setDisplayWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM); + mStack.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN); + assertFalse(activity.shouldUseSizeCompatMode()); + } + /** * Setup {@link #mActivity} as a size-compat-mode-able activity with fixed aspect and/or * orientation. diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java index f6699faf7a61..5a92d6849434 100644 --- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java +++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java @@ -392,9 +392,6 @@ public class RollbackTest { RollbackManager.PROPERTY_ROLLBACK_LIFETIME_MILLIS, Long.toString(expirationTime), false /* makeDefault*/); - // Pull the new expiration time from DeviceConfig - rm.reloadPersistedData(); - // Uninstall TestApp.A Uninstall.packages(TestApp.A); assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1); @@ -457,9 +454,6 @@ public class RollbackTest { RollbackManager.PROPERTY_ROLLBACK_LIFETIME_MILLIS, Long.toString(expirationTime), false /* makeDefault*/); - // Pull the new expiration time from DeviceConfig - rm.reloadPersistedData(); - // Install app A with rollback enabled Uninstall.packages(TestApp.A); Install.single(TestApp.A1).commit(); diff --git a/tests/testables/src/android/testing/TestableLooper.java b/tests/testables/src/android/testing/TestableLooper.java index 8d99ac7100eb..8eac3ea13a23 100644 --- a/tests/testables/src/android/testing/TestableLooper.java +++ b/tests/testables/src/android/testing/TestableLooper.java @@ -234,6 +234,7 @@ public class TestableLooper { try { mLooper = setAsMain ? Looper.getMainLooper() : createLooper(); mTestableLooper = new TestableLooper(mLooper, false); + mTestableLooper.getLooper().getThread().setName(test.getClass().getName()); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/wifi/jarjar-rules.txt b/wifi/jarjar-rules.txt index 60a38dd827fa..8f720407f4d7 100644 --- a/wifi/jarjar-rules.txt +++ b/wifi/jarjar-rules.txt @@ -1,6 +1,5 @@ rule android.net.InterfaceConfigurationParcel* @0 rule android.net.InterfaceConfiguration* com.android.server.x.wifi.net.InterfaceConfiguration@1 -rule android.net.util.** com.android.server.x.wifi.net.util.@1 # We don't jar-jar the entire package because, we still use some classes (like # AsyncChannel in com.android.internal.util) from these packages which are not |