diff options
28 files changed, 127 insertions, 4635 deletions
diff --git a/Android.bp b/Android.bp index ceb35bd6eb66..b0a1f93acd97 100644 --- a/Android.bp +++ b/Android.bp @@ -314,6 +314,7 @@ java_defaults { "tv_tuner_resource_manager_aidl_interface-java", "soundtrigger_middleware-aidl-java", "modules-utils-preconditions", + "modules-utils-statemachine", "modules-utils-synchronous-result-receiver", "modules-utils-os", "framework-permission-aidl-java", diff --git a/ProtoLibraries.bp b/ProtoLibraries.bp index d18154a761ec..de84792c45bb 100644 --- a/ProtoLibraries.bp +++ b/ProtoLibraries.bp @@ -14,7 +14,6 @@ gensrcs { name: "framework-javastream-protos", - depfile: true, tools: [ "aprotoc", @@ -22,10 +21,13 @@ gensrcs { "soong_zip", ], + tool_files: [ + ":libprotobuf-internal-protos", + ], + cmd: "mkdir -p $(genDir)/$(in) " + "&& $(location aprotoc) " + " --plugin=$(location protoc-gen-javastream) " + - " --dependency_out=$(depfile) " + " --javastream_out=$(genDir)/$(in) " + " -Iexternal/protobuf/src " + " -I . " + @@ -47,17 +49,19 @@ gensrcs { gensrcs { name: "framework-cppstream-protos", - depfile: true, tools: [ "aprotoc", "protoc-gen-cppstream", ], + tool_files: [ + ":libprotobuf-internal-protos", + ], + cmd: "mkdir -p $(genDir) " + "&& $(location aprotoc) " + " --plugin=$(location protoc-gen-cppstream) " + - " --dependency_out=$(depfile) " + " --cppstream_out=$(genDir) " + " -Iexternal/protobuf/src " + " -I . " + diff --git a/boot/hiddenapi/hiddenapi-max-target-q.txt b/boot/hiddenapi/hiddenapi-max-target-q.txt index 4832dd184ec5..fdda4ea1386e 100644 --- a/boot/hiddenapi/hiddenapi-max-target-q.txt +++ b/boot/hiddenapi/hiddenapi-max-target-q.txt @@ -404,16 +404,11 @@ Lcom/android/internal/R$raw;->nodomain:I Lcom/android/internal/R$string;->byteShort:I Lcom/android/internal/R$string;->cancel:I Lcom/android/internal/R$string;->enable_explore_by_touch_warning_title:I -Lcom/android/internal/R$string;->gigabyteShort:I -Lcom/android/internal/R$string;->kilobyteShort:I -Lcom/android/internal/R$string;->megabyteShort:I Lcom/android/internal/R$string;->notification_title:I Lcom/android/internal/R$string;->no_matches:I Lcom/android/internal/R$string;->ok:I -Lcom/android/internal/R$string;->petabyteShort:I Lcom/android/internal/R$string;->redo:I Lcom/android/internal/R$string;->share:I -Lcom/android/internal/R$string;->terabyteShort:I Lcom/android/internal/R$string;->whichApplication:I Lcom/android/internal/R$style;->Animation_DropDownDown:I Lcom/android/internal/R$style;->Animation_DropDownUp:I diff --git a/core/api/current.txt b/core/api/current.txt index 2e0e6e571b87..dc1949693802 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -30355,6 +30355,7 @@ package android.os { method public <T extends android.os.Parcelable> void writeTypedArray(@Nullable T[], int); method public <T extends android.os.Parcelable> void writeTypedArrayMap(@Nullable android.util.ArrayMap<java.lang.String,T>, int); method public <T extends android.os.Parcelable> void writeTypedList(@Nullable java.util.List<T>); + method public <T extends android.os.Parcelable> void writeTypedList(@Nullable java.util.List<T>, int); method public <T extends android.os.Parcelable> void writeTypedObject(@Nullable T, int); method public <T extends android.os.Parcelable> void writeTypedSparseArray(@Nullable android.util.SparseArray<T>, int); method public void writeValue(@Nullable Object); diff --git a/core/java/Android.bp b/core/java/Android.bp index 8081c153255d..a643c29b1da1 100644 --- a/core/java/Android.bp +++ b/core/java/Android.bp @@ -381,27 +381,6 @@ filegroup { ], } -java_library { - name: "modules-utils-statemachine", - srcs: [ - "com/android/internal/util/IState.java", - "com/android/internal/util/State.java", - "com/android/internal/util/StateMachine.java", - ], - libs: [ - "framework-annotations-lib", - "unsupportedappusage", - ], - sdk_version: "module_current", - min_sdk_version: "29", - - visibility: ["//visibility:public"], - apex_available: [ - "//apex_available:anyapex", - "//apex_available:platform", - ], -} - filegroup { name: "framework-ims-common-shared-srcs", srcs: [ diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 8bbfd8dfaedf..b601420d7d4a 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -244,6 +244,7 @@ import java.util.Map; import java.util.Objects; import java.util.TimeZone; import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; /** @@ -345,11 +346,9 @@ public final class ActivityThread extends ClientTransactionHandler */ @UnsupportedAppUsage final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>(); - /** - * Maps from activity token to local record of the activities that are preparing to be launched. - */ - final Map<IBinder, ActivityClientRecord> mLaunchingActivities = - Collections.synchronizedMap(new ArrayMap<IBinder, ActivityClientRecord>()); + /** Maps from activity token to the pending override configuration. */ + @GuardedBy("mPendingOverrideConfigs") + private final ArrayMap<IBinder, Configuration> mPendingOverrideConfigs = new ArrayMap<>(); /** The activities to be truly destroyed (not include relaunch). */ final Map<IBinder, ClientTransactionItem> mActivitiesToBeDestroyed = Collections.synchronizedMap(new ArrayMap<IBinder, ClientTransactionItem>()); @@ -359,6 +358,7 @@ public final class ActivityThread extends ClientTransactionHandler // Number of activities that are currently visible on-screen. @UnsupportedAppUsage int mNumVisibleActivities = 0; + private final AtomicInteger mNumLaunchingActivities = new AtomicInteger(); @GuardedBy("mAppThread") private int mLastProcessState = PROCESS_STATE_UNKNOWN; @GuardedBy("mAppThread") @@ -556,10 +556,6 @@ public final class ActivityThread extends ClientTransactionHandler boolean hideForNow; Configuration createdConfig; Configuration overrideConfig; - // Used to save the last reported configuration from server side so that activity - // configuration transactions can always use the latest configuration. - @GuardedBy("this") - private Configuration mPendingOverrideConfig; // Used for consolidating configs before sending on to Activity. private Configuration tmpConfig = new Configuration(); // Callback used for updating activity override config. @@ -3330,21 +3326,6 @@ public final class ActivityThread extends ClientTransactionHandler } @Override - public void addLaunchingActivity(IBinder token, ActivityClientRecord activity) { - mLaunchingActivities.put(token, activity); - } - - @Override - public ActivityClientRecord getLaunchingActivity(IBinder token) { - return mLaunchingActivities.get(token); - } - - @Override - public void removeLaunchingActivity(IBinder token) { - mLaunchingActivities.remove(token); - } - - @Override public ActivityClientRecord getActivityClient(IBinder token) { return mActivities.get(token); } @@ -3388,7 +3369,7 @@ public final class ActivityThread extends ClientTransactionHandler // Defer the top state for VM to avoid aggressive JIT compilation affecting activity // launch time. if (processState == ActivityManager.PROCESS_STATE_TOP - && !mLaunchingActivities.isEmpty()) { + && mNumLaunchingActivities.get() > 0) { mPendingProcessState = processState; mH.postDelayed(this::applyPendingProcessState, PENDING_TOP_PROCESS_STATE_TIMEOUT); } else { @@ -3404,7 +3385,7 @@ public final class ActivityThread extends ClientTransactionHandler // Handle the pending configuration if the process state is changed from cached to // non-cached. Except the case where there is a launching activity because the // LaunchActivityItem will handle it. - if (wasCached && !isCachedProcessState() && mLaunchingActivities.isEmpty()) { + if (wasCached && !isCachedProcessState() && mNumLaunchingActivities.get() == 0) { final Configuration pendingConfig = mConfigurationController.getPendingConfiguration(false /* clearPending */); if (pendingConfig == null) { @@ -3442,6 +3423,11 @@ public final class ActivityThread extends ClientTransactionHandler } } + @Override + public void countLaunchingActivities(int num) { + mNumLaunchingActivities.getAndAdd(num); + } + @UnsupportedAppUsage public final void sendActivityResult( IBinder token, String id, int requestCode, @@ -6071,31 +6057,31 @@ public final class ActivityThread extends ClientTransactionHandler } /** - * Sets the supplied {@code overrideConfig} as pending for the {@code activityToken}. Calling + * Sets the supplied {@code overrideConfig} as pending for the {@code token}. Calling * this method prevents any calls to * {@link #handleActivityConfigurationChanged(ActivityClientRecord, Configuration, int)} from * processing any configurations older than {@code overrideConfig}. */ @Override - public void updatePendingActivityConfiguration(ActivityClientRecord r, - Configuration overrideConfig) { - synchronized (r) { - if (r.mPendingOverrideConfig != null - && !r.mPendingOverrideConfig.isOtherSeqNewer(overrideConfig)) { + public void updatePendingActivityConfiguration(IBinder token, Configuration overrideConfig) { + synchronized (mPendingOverrideConfigs) { + final Configuration pendingOverrideConfig = mPendingOverrideConfigs.get(token); + if (pendingOverrideConfig != null + && !pendingOverrideConfig.isOtherSeqNewer(overrideConfig)) { if (DEBUG_CONFIGURATION) { - Slog.v(TAG, "Activity has newer configuration pending so drop this" - + " transaction. overrideConfig=" + overrideConfig - + " r.mPendingOverrideConfig=" + r.mPendingOverrideConfig); + Slog.v(TAG, "Activity has newer configuration pending so this transaction will" + + " be dropped. overrideConfig=" + overrideConfig + + " pendingOverrideConfig=" + pendingOverrideConfig); } return; } - r.mPendingOverrideConfig = overrideConfig; + mPendingOverrideConfigs.put(token, overrideConfig); } } /** * Handle new activity configuration and/or move to a different display. This method is a noop - * if {@link #updatePendingActivityConfiguration(ActivityClientRecord, Configuration)} has been + * if {@link #updatePendingActivityConfiguration(IBinder, Configuration)} has been * called with a newer config than {@code overrideConfig}. * * @param r Target activity record. @@ -6106,16 +6092,17 @@ public final class ActivityThread extends ClientTransactionHandler @Override public void handleActivityConfigurationChanged(ActivityClientRecord r, @NonNull Configuration overrideConfig, int displayId) { - synchronized (r) { - if (overrideConfig.isOtherSeqNewer(r.mPendingOverrideConfig)) { + synchronized (mPendingOverrideConfigs) { + final Configuration pendingOverrideConfig = mPendingOverrideConfigs.get(r.token); + if (overrideConfig.isOtherSeqNewer(pendingOverrideConfig)) { if (DEBUG_CONFIGURATION) { Slog.v(TAG, "Activity has newer configuration pending so drop this" + " transaction. overrideConfig=" + overrideConfig - + " r.mPendingOverrideConfig=" + r.mPendingOverrideConfig); + + " pendingOverrideConfig=" + pendingOverrideConfig); } return; } - r.mPendingOverrideConfig = null; + mPendingOverrideConfigs.remove(r.token); } if (displayId == INVALID_DISPLAY) { diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java index c743f6572d5e..d365269ed1a6 100644 --- a/core/java/android/app/ClientTransactionHandler.java +++ b/core/java/android/app/ClientTransactionHandler.java @@ -83,6 +83,9 @@ public abstract class ClientTransactionHandler { /** Set current process state. */ public abstract void updateProcessState(int processState, boolean fromIpc); + /** Count how many activities are launching. */ + public abstract void countLaunchingActivities(int num); + // Execute phase related logic and handlers. Methods here execute actual lifecycle transactions // and deliver callbacks. @@ -139,7 +142,7 @@ public abstract class ClientTransactionHandler { public abstract void performRestartActivity(@NonNull ActivityClientRecord r, boolean start); /** Set pending activity configuration in case it will be updated by other transaction item. */ - public abstract void updatePendingActivityConfiguration(@NonNull ActivityClientRecord r, + public abstract void updatePendingActivityConfiguration(@NonNull IBinder token, Configuration overrideConfig); /** Deliver activity (override) configuration change. */ @@ -189,26 +192,6 @@ public abstract class ClientTransactionHandler { FixedRotationAdjustments fixedRotationAdjustments); /** - * Add {@link ActivityClientRecord} that is preparing to be launched. - * @param token Activity token. - * @param activity An initialized instance of {@link ActivityClientRecord} to use during launch. - */ - public abstract void addLaunchingActivity(IBinder token, ActivityClientRecord activity); - - /** - * Get {@link ActivityClientRecord} that is preparing to be launched. - * @param token Activity token. - * @return An initialized instance of {@link ActivityClientRecord} to use during launch. - */ - public abstract ActivityClientRecord getLaunchingActivity(IBinder token); - - /** - * Remove {@link ActivityClientRecord} from the launching activity list. - * @param token Activity token. - */ - public abstract void removeLaunchingActivity(IBinder token); - - /** * Get {@link android.app.ActivityThread.ActivityClientRecord} instance that corresponds to the * provided token. */ diff --git a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java index 032b57e65458..5a3ad310b4d6 100644 --- a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java +++ b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java @@ -40,11 +40,9 @@ public class ActivityConfigurationChangeItem extends ActivityTransactionItem { @Override public void preExecute(android.app.ClientTransactionHandler client, IBinder token) { - final ActivityClientRecord r = getActivityClientRecord(client, token, - true /* includeLaunching */); // Notify the client of an upcoming change in the token configuration. This ensures that // batches of config change items only process the newest configuration. - client.updatePendingActivityConfiguration(r, mConfiguration); + client.updatePendingActivityConfiguration(token, mConfiguration); } @Override diff --git a/core/java/android/app/servertransaction/ActivityTransactionItem.java b/core/java/android/app/servertransaction/ActivityTransactionItem.java index 186f25deab67..6a6d76d20259 100644 --- a/core/java/android/app/servertransaction/ActivityTransactionItem.java +++ b/core/java/android/app/servertransaction/ActivityTransactionItem.java @@ -53,43 +53,23 @@ public abstract class ActivityTransactionItem extends ClientTransactionItem { public abstract void execute(@NonNull ClientTransactionHandler client, @NonNull ActivityClientRecord r, PendingTransactionActions pendingActions); - @NonNull ActivityClientRecord getActivityClientRecord( - @NonNull ClientTransactionHandler client, IBinder token) { - return getActivityClientRecord(client, token, false /* includeLaunching */); - } - /** * Gets the {@link ActivityClientRecord} instance that corresponds to the provided token. * @param client Target client handler. * @param token Target activity token. - * @param includeLaunching Indicate to find the {@link ActivityClientRecord} in launching - * activity list. - * <p>Note that there is no {@link android.app.Activity} instance in - * {@link ActivityClientRecord} from the launching activity list. * @return The {@link ActivityClientRecord} instance that corresponds to the provided token. */ @NonNull ActivityClientRecord getActivityClientRecord( - @NonNull ClientTransactionHandler client, IBinder token, boolean includeLaunching) { - ActivityClientRecord r = null; - // Check launching Activity first to prevent race condition that activity instance has not - // yet set to ActivityClientRecord. - if (includeLaunching) { - r = client.getLaunchingActivity(token); - } - // Then if we don't want to find launching Activity or the ActivityClientRecord doesn't - // exist in launching Activity list. The ActivityClientRecord should have been initialized - // and put in the Activity list. - if (r == null) { - r = client.getActivityClient(token); - if (r != null && client.getActivity(token) == null) { - throw new IllegalArgumentException("Activity must not be null to execute " - + "transaction item"); - } - } + @NonNull ClientTransactionHandler client, IBinder token) { + final ActivityClientRecord r = client.getActivityClient(token); if (r == null) { throw new IllegalArgumentException("Activity client record must not be null to execute " + "transaction item"); } + if (client.getActivity(token) == null) { + throw new IllegalArgumentException("Activity must not be null to execute " + + "transaction item"); + } return r; } } diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java index 34e4fcdb9140..e281a0268184 100644 --- a/core/java/android/app/servertransaction/LaunchActivityItem.java +++ b/core/java/android/app/servertransaction/LaunchActivityItem.java @@ -82,12 +82,7 @@ public class LaunchActivityItem extends ClientTransactionItem { @Override public void preExecute(ClientTransactionHandler client, IBinder token) { - ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo, - mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState, - mPendingResults, mPendingNewIntents, mActivityOptions, mIsForward, mProfilerInfo, - client, mAssistToken, mFixedRotationAdjustments, mShareableActivityToken, - mLaunchedFromBubble); - client.addLaunchingActivity(token, r); + client.countLaunchingActivities(1); client.updateProcessState(mProcState, false); client.updatePendingConfiguration(mCurConfig); if (mActivityClientController != null) { @@ -99,7 +94,11 @@ public class LaunchActivityItem extends ClientTransactionItem { public void execute(ClientTransactionHandler client, IBinder token, PendingTransactionActions pendingActions) { Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart"); - ActivityClientRecord r = client.getLaunchingActivity(token); + ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo, + mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState, + mPendingResults, mPendingNewIntents, mActivityOptions, mIsForward, mProfilerInfo, + client, mAssistToken, mFixedRotationAdjustments, mShareableActivityToken, + mLaunchedFromBubble); client.handleLaunchActivity(r, pendingActions, null /* customIntent */); Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); } @@ -107,7 +106,7 @@ public class LaunchActivityItem extends ClientTransactionItem { @Override public void postExecute(ClientTransactionHandler client, IBinder token, PendingTransactionActions pendingActions) { - client.removeLaunchingActivity(token); + client.countLaunchingActivities(-1); } diff --git a/core/java/android/app/servertransaction/MoveToDisplayItem.java b/core/java/android/app/servertransaction/MoveToDisplayItem.java index 4b8a3476262e..2893ff21c3f2 100644 --- a/core/java/android/app/servertransaction/MoveToDisplayItem.java +++ b/core/java/android/app/servertransaction/MoveToDisplayItem.java @@ -40,11 +40,9 @@ public class MoveToDisplayItem extends ActivityTransactionItem { @Override public void preExecute(ClientTransactionHandler client, IBinder token) { - final ActivityClientRecord r = getActivityClientRecord(client, token, - true /* includeLaunching */); // Notify the client of an upcoming change in the token configuration. This ensures that // batches of config change items only process the newest configuration. - client.updatePendingActivityConfiguration(r, mConfiguration); + client.updatePendingActivityConfiguration(token, mConfiguration); } @Override diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java index e5dab0539a8e..0418a4bb9f80 100644 --- a/core/java/android/os/BaseBundle.java +++ b/core/java/android/os/BaseBundle.java @@ -31,6 +31,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.IndentingPrintWriter; import java.io.Serializable; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Set; import java.util.function.BiFunction; @@ -102,7 +103,7 @@ public class BaseBundle { /* * If mParcelledData is non-null, then mMap will be null and the * data are stored as a Parcel containing a Bundle. When the data - * are unparcelled, mParcelledData willbe set to null. + * are unparcelled, mParcelledData will be set to null. */ @UnsupportedAppUsage volatile Parcel mParcelledData = null; @@ -112,6 +113,19 @@ public class BaseBundle { */ private boolean mParcelledByNative; + /* + * Flag indicating if mParcelledData is only referenced in this bundle. + * mParcelledData could be referenced by other bundles if mMap contains lazy values, + * and bundle data is copied to another bundle using putAll or the copy constructors. + */ + boolean mOwnsLazyValues = true; + + /* + * As mParcelledData is set to null when it is unparcelled, we keep a weak reference to + * it to aid in recycling it. Do not use this reference otherwise. + */ + private WeakReference<Parcel> mWeakParcelledData = null; + /** * The ClassLoader used when unparcelling data from mParcelledData. */ @@ -200,6 +214,9 @@ public class BaseBundle { mClassLoader = from.mClassLoader; if (from.mMap != null) { + mOwnsLazyValues = false; + from.mOwnsLazyValues = false; + if (!deep) { mMap = new ArrayMap<>(from.mMap); } else { @@ -434,6 +451,9 @@ public class BaseBundle { mMap = map; if (recycleParcel) { recycleParcel(parcelledData); + mWeakParcelledData = null; + } else { + mWeakParcelledData = new WeakReference<>(parcelledData); } mParcelledByNative = false; mParcelledData = null; @@ -575,6 +595,10 @@ public class BaseBundle { */ public void clear() { unparcel(); + if (mOwnsLazyValues && mWeakParcelledData != null) { + recycleParcel(mWeakParcelledData.get()); + mWeakParcelledData = null; + } mMap.clear(); } diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java index cf28c1639fac..7e355d95cdb3 100644 --- a/core/java/android/os/Bundle.java +++ b/core/java/android/os/Bundle.java @@ -301,6 +301,8 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable { public void putAll(Bundle bundle) { unparcel(); bundle.unparcel(); + mOwnsLazyValues = false; + bundle.mOwnsLazyValues = false; mMap.putAll(bundle.mMap); // FD state is now known if and only if both bundles already knew diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java index 7baff1a3622f..6411f424e7e0 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -1999,7 +1999,20 @@ public final class Parcel { } /** - * @hide + * Flatten a List containing a particular object type into the parcel, at + * the current dataPosition() and growing dataCapacity() if needed. The + * type of the objects in the list must be one that implements Parcelable. + * Unlike the generic writeList() method, however, only the raw data of the + * objects is written and not their type, so you must use the corresponding + * readTypedList() to unmarshall them. + * + * @param val The list of objects to be written. + * @param parcelableFlags Contextual flags as per + * {@link Parcelable#writeToParcel(Parcel, int) Parcelable.writeToParcel()}. + * + * @see #createTypedArrayList + * @see #readTypedList + * @see Parcelable */ public <T extends Parcelable> void writeTypedList(@Nullable List<T> val, int parcelableFlags) { if (val == null) { diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index d4fc0c6336ba..ed6cd18941e3 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -1035,7 +1035,6 @@ public final class ViewRootImpl implements ViewParent, if (mView == null) { mView = view; - mAttachInfo.mDisplayState = mDisplay.getState(); mViewLayoutDirectionInitial = mView.getRawLayoutDirection(); mFallbackEventHandler.setView(view); mWindowAttributes.copyFrom(attrs); @@ -1229,6 +1228,9 @@ public final class ViewRootImpl implements ViewParent, } registerListeners(); + // We should update mAttachInfo.mDisplayState after registerDisplayListener + // because displayState might be changed before registerDisplayListener. + mAttachInfo.mDisplayState = mDisplay.getState(); if ((res & WindowManagerGlobal.ADD_FLAG_USE_BLAST) != 0) { mUseBLASTAdapter = true; } diff --git a/core/java/com/android/internal/util/IState.java b/core/java/com/android/internal/util/IState.java deleted file mode 100644 index 41b3d5e0a11f..000000000000 --- a/core/java/com/android/internal/util/IState.java +++ /dev/null @@ -1,73 +0,0 @@ -/** - * Copyright (C) 2011 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.internal.util; - -import android.compat.annotation.UnsupportedAppUsage; -import android.os.Message; - -/** - * {@hide} - * - * The interface for implementing states in a {@link StateMachine} - */ -public interface IState { - - /** - * Returned by processMessage to indicate the message was processed. - */ - static final boolean HANDLED = true; - - /** - * Returned by processMessage to indicate the message was NOT processed. - */ - static final boolean NOT_HANDLED = false; - - /** - * Called when a state is entered. - */ - void enter(); - - /** - * Called when a state is exited. - */ - void exit(); - - /** - * Called when a message is to be processed by the - * state machine. - * - * This routine is never reentered thus no synchronization - * is needed as only one processMessage method will ever be - * executing within a state machine at any given time. This - * does mean that processing by this routine must be completed - * as expeditiously as possible as no subsequent messages will - * be processed until this routine returns. - * - * @param msg to process - * @return HANDLED if processing has completed and NOT_HANDLED - * if the message wasn't processed. - */ - boolean processMessage(Message msg); - - /** - * Name of State for debugging purposes. - * - * @return name of state. - */ - @UnsupportedAppUsage - String getName(); -} diff --git a/core/java/com/android/internal/util/State.java b/core/java/com/android/internal/util/State.java deleted file mode 100644 index d5c0f60f4b37..000000000000 --- a/core/java/com/android/internal/util/State.java +++ /dev/null @@ -1,83 +0,0 @@ -/** - * Copyright (C) 2009 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.internal.util; - -import android.annotation.SuppressLint; -import android.compat.annotation.UnsupportedAppUsage; -import android.os.Build; -import android.os.Message; - -/** - * {@hide} - * - * The class for implementing states in a StateMachine - */ -@SuppressLint("AndroidFrameworkRequiresPermission") -public class State implements IState { - - /** - * Constructor - */ - @UnsupportedAppUsage - protected State() { - } - - /* (non-Javadoc) - * @see com.android.internal.util.IState#enter() - */ - @UnsupportedAppUsage - @Override - public void enter() { - } - - /* (non-Javadoc) - * @see com.android.internal.util.IState#exit() - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @Override - public void exit() { - } - - /* (non-Javadoc) - * @see com.android.internal.util.IState#processMessage(android.os.Message) - */ - @UnsupportedAppUsage - @Override - public boolean processMessage(Message msg) { - return false; - } - - /** - * Name of State for debugging purposes. - * - * This default implementation returns the class name, returning - * the instance name would better in cases where a State class - * is used for multiple states. But normally there is one class per - * state and the class name is sufficient and easy to get. You may - * want to provide a setName or some other mechanism for setting - * another name if the class name is not appropriate. - * - * @see com.android.internal.util.IState#processMessage(android.os.Message) - */ - @UnsupportedAppUsage - @Override - public String getName() { - String name = getClass().getName(); - int lastDollar = name.lastIndexOf('$'); - return name.substring(lastDollar + 1); - } -} diff --git a/core/java/com/android/internal/util/StateMachine.java b/core/java/com/android/internal/util/StateMachine.java deleted file mode 100644 index cb8d9d127415..000000000000 --- a/core/java/com/android/internal/util/StateMachine.java +++ /dev/null @@ -1,2185 +0,0 @@ -/** - * Copyright (C) 2009 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.internal.util; - -import android.compat.annotation.UnsupportedAppUsage; -import android.os.Build; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.Looper; -import android.os.Message; -import android.text.TextUtils; -import android.util.Log; - -import com.android.internal.annotations.VisibleForTesting; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Collection; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Vector; - -/** - * {@hide} - * - * <p>The state machine defined here is a hierarchical state machine which processes messages - * and can have states arranged hierarchically.</p> - * - * <p>A state is a <code>State</code> object and must implement - * <code>processMessage</code> and optionally <code>enter/exit/getName</code>. - * The enter/exit methods are equivalent to the construction and destruction - * in Object Oriented programming and are used to perform initialization and - * cleanup of the state respectively. The <code>getName</code> method returns the - * name of the state; the default implementation returns the class name. It may be - * desirable to have <code>getName</code> return the state instance name instead, - * in particular if a particular state class has multiple instances.</p> - * - * <p>When a state machine is created, <code>addState</code> is used to build the - * hierarchy and <code>setInitialState</code> is used to identify which of these - * is the initial state. After construction the programmer calls <code>start</code> - * which initializes and starts the state machine. The first action the StateMachine - * is to the invoke <code>enter</code> for all of the initial state's hierarchy, - * starting at its eldest parent. The calls to enter will be done in the context - * of the StateMachine's Handler, not in the context of the call to start, and they - * will be invoked before any messages are processed. For example, given the simple - * state machine below, mP1.enter will be invoked and then mS1.enter. Finally, - * messages sent to the state machine will be processed by the current state; - * in our simple state machine below that would initially be mS1.processMessage.</p> -<pre> - mP1 - / \ - mS2 mS1 ----> initial state -</pre> - * <p>After the state machine is created and started, messages are sent to a state - * machine using <code>sendMessage</code> and the messages are created using - * <code>obtainMessage</code>. When the state machine receives a message the - * current state's <code>processMessage</code> is invoked. In the above example - * mS1.processMessage will be invoked first. The state may use <code>transitionTo</code> - * to change the current state to a new state.</p> - * - * <p>Each state in the state machine may have a zero or one parent states. If - * a child state is unable to handle a message it may have the message processed - * by its parent by returning false or NOT_HANDLED. If a message is not handled by - * a child state or any of its ancestors, <code>unhandledMessage</code> will be invoked - * to give one last chance for the state machine to process the message.</p> - * - * <p>When all processing is completed a state machine may choose to call - * <code>transitionToHaltingState</code>. When the current <code>processingMessage</code> - * returns the state machine will transfer to an internal <code>HaltingState</code> - * and invoke <code>halting</code>. Any message subsequently received by the state - * machine will cause <code>haltedProcessMessage</code> to be invoked.</p> - * - * <p>If it is desirable to completely stop the state machine call <code>quit</code> or - * <code>quitNow</code>. These will call <code>exit</code> of the current state and its parents, - * call <code>onQuitting</code> and then exit Thread/Loopers.</p> - * - * <p>In addition to <code>processMessage</code> each <code>State</code> has - * an <code>enter</code> method and <code>exit</code> method which may be overridden.</p> - * - * <p>Since the states are arranged in a hierarchy transitioning to a new state - * causes current states to be exited and new states to be entered. To determine - * the list of states to be entered/exited the common parent closest to - * the current state is found. We then exit from the current state and its - * parent's up to but not including the common parent state and then enter all - * of the new states below the common parent down to the destination state. - * If there is no common parent all states are exited and then the new states - * are entered.</p> - * - * <p>Two other methods that states can use are <code>deferMessage</code> and - * <code>sendMessageAtFrontOfQueue</code>. The <code>sendMessageAtFrontOfQueue</code> sends - * a message but places it on the front of the queue rather than the back. The - * <code>deferMessage</code> causes the message to be saved on a list until a - * transition is made to a new state. At which time all of the deferred messages - * will be put on the front of the state machine queue with the oldest message - * at the front. These will then be processed by the new current state before - * any other messages that are on the queue or might be added later. Both of - * these are protected and may only be invoked from within a state machine.</p> - * - * <p>To illustrate some of these properties we'll use state machine with an 8 - * state hierarchy:</p> -<pre> - mP0 - / \ - mP1 mS0 - / \ - mS2 mS1 - / \ \ - mS3 mS4 mS5 ---> initial state -</pre> - * <p>After starting mS5 the list of active states is mP0, mP1, mS1 and mS5. - * So the order of calling processMessage when a message is received is mS5, - * mS1, mP1, mP0 assuming each processMessage indicates it can't handle this - * message by returning false or NOT_HANDLED.</p> - * - * <p>Now assume mS5.processMessage receives a message it can handle, and during - * the handling determines the machine should change states. It could call - * transitionTo(mS4) and return true or HANDLED. Immediately after returning from - * processMessage the state machine runtime will find the common parent, - * which is mP1. It will then call mS5.exit, mS1.exit, mS2.enter and then - * mS4.enter. The new list of active states is mP0, mP1, mS2 and mS4. So - * when the next message is received mS4.processMessage will be invoked.</p> - * - * <p>Now for some concrete examples, here is the canonical HelloWorld as a state machine. - * It responds with "Hello World" being printed to the log for every message.</p> -<pre> -class HelloWorld extends StateMachine { - HelloWorld(String name) { - super(name); - addState(mState1); - setInitialState(mState1); - } - - public static HelloWorld makeHelloWorld() { - HelloWorld hw = new HelloWorld("hw"); - hw.start(); - return hw; - } - - class State1 extends State { - @Override public boolean processMessage(Message message) { - log("Hello World"); - return HANDLED; - } - } - State1 mState1 = new State1(); -} - -void testHelloWorld() { - HelloWorld hw = makeHelloWorld(); - hw.sendMessage(hw.obtainMessage()); -} -</pre> - * <p>A more interesting state machine is one with four states - * with two independent parent states.</p> -<pre> - mP1 mP2 - / \ - mS2 mS1 -</pre> - * <p>Here is a description of this state machine using pseudo code.</p> - <pre> -state mP1 { - enter { log("mP1.enter"); } - exit { log("mP1.exit"); } - on msg { - CMD_2 { - send(CMD_3); - defer(msg); - transitionTo(mS2); - return HANDLED; - } - return NOT_HANDLED; - } -} - -INITIAL -state mS1 parent mP1 { - enter { log("mS1.enter"); } - exit { log("mS1.exit"); } - on msg { - CMD_1 { - transitionTo(mS1); - return HANDLED; - } - return NOT_HANDLED; - } -} - -state mS2 parent mP1 { - enter { log("mS2.enter"); } - exit { log("mS2.exit"); } - on msg { - CMD_2 { - send(CMD_4); - return HANDLED; - } - CMD_3 { - defer(msg); - transitionTo(mP2); - return HANDLED; - } - return NOT_HANDLED; - } -} - -state mP2 { - enter { - log("mP2.enter"); - send(CMD_5); - } - exit { log("mP2.exit"); } - on msg { - CMD_3, CMD_4 { return HANDLED; } - CMD_5 { - transitionTo(HaltingState); - return HANDLED; - } - return NOT_HANDLED; - } -} -</pre> - * <p>The implementation is below and also in StateMachineTest:</p> -<pre> -class Hsm1 extends StateMachine { - public static final int CMD_1 = 1; - public static final int CMD_2 = 2; - public static final int CMD_3 = 3; - public static final int CMD_4 = 4; - public static final int CMD_5 = 5; - - public static Hsm1 makeHsm1() { - log("makeHsm1 E"); - Hsm1 sm = new Hsm1("hsm1"); - sm.start(); - log("makeHsm1 X"); - return sm; - } - - Hsm1(String name) { - super(name); - log("ctor E"); - - // Add states, use indentation to show hierarchy - addState(mP1); - addState(mS1, mP1); - addState(mS2, mP1); - addState(mP2); - - // Set the initial state - setInitialState(mS1); - log("ctor X"); - } - - class P1 extends State { - @Override public void enter() { - log("mP1.enter"); - } - @Override public boolean processMessage(Message message) { - boolean retVal; - log("mP1.processMessage what=" + message.what); - switch(message.what) { - case CMD_2: - // CMD_2 will arrive in mS2 before CMD_3 - sendMessage(obtainMessage(CMD_3)); - deferMessage(message); - transitionTo(mS2); - retVal = HANDLED; - break; - default: - // Any message we don't understand in this state invokes unhandledMessage - retVal = NOT_HANDLED; - break; - } - return retVal; - } - @Override public void exit() { - log("mP1.exit"); - } - } - - class S1 extends State { - @Override public void enter() { - log("mS1.enter"); - } - @Override public boolean processMessage(Message message) { - log("S1.processMessage what=" + message.what); - if (message.what == CMD_1) { - // Transition to ourself to show that enter/exit is called - transitionTo(mS1); - return HANDLED; - } else { - // Let parent process all other messages - return NOT_HANDLED; - } - } - @Override public void exit() { - log("mS1.exit"); - } - } - - class S2 extends State { - @Override public void enter() { - log("mS2.enter"); - } - @Override public boolean processMessage(Message message) { - boolean retVal; - log("mS2.processMessage what=" + message.what); - switch(message.what) { - case(CMD_2): - sendMessage(obtainMessage(CMD_4)); - retVal = HANDLED; - break; - case(CMD_3): - deferMessage(message); - transitionTo(mP2); - retVal = HANDLED; - break; - default: - retVal = NOT_HANDLED; - break; - } - return retVal; - } - @Override public void exit() { - log("mS2.exit"); - } - } - - class P2 extends State { - @Override public void enter() { - log("mP2.enter"); - sendMessage(obtainMessage(CMD_5)); - } - @Override public boolean processMessage(Message message) { - log("P2.processMessage what=" + message.what); - switch(message.what) { - case(CMD_3): - break; - case(CMD_4): - break; - case(CMD_5): - transitionToHaltingState(); - break; - } - return HANDLED; - } - @Override public void exit() { - log("mP2.exit"); - } - } - - @Override - void onHalting() { - log("halting"); - synchronized (this) { - this.notifyAll(); - } - } - - P1 mP1 = new P1(); - S1 mS1 = new S1(); - S2 mS2 = new S2(); - P2 mP2 = new P2(); -} -</pre> - * <p>If this is executed by sending two messages CMD_1 and CMD_2 - * (Note the synchronize is only needed because we use hsm.wait())</p> -<pre> -Hsm1 hsm = makeHsm1(); -synchronize(hsm) { - hsm.sendMessage(obtainMessage(hsm.CMD_1)); - hsm.sendMessage(obtainMessage(hsm.CMD_2)); - try { - // wait for the messages to be handled - hsm.wait(); - } catch (InterruptedException e) { - loge("exception while waiting " + e.getMessage()); - } -} -</pre> - * <p>The output is:</p> -<pre> -D/hsm1 ( 1999): makeHsm1 E -D/hsm1 ( 1999): ctor E -D/hsm1 ( 1999): ctor X -D/hsm1 ( 1999): mP1.enter -D/hsm1 ( 1999): mS1.enter -D/hsm1 ( 1999): makeHsm1 X -D/hsm1 ( 1999): mS1.processMessage what=1 -D/hsm1 ( 1999): mS1.exit -D/hsm1 ( 1999): mS1.enter -D/hsm1 ( 1999): mS1.processMessage what=2 -D/hsm1 ( 1999): mP1.processMessage what=2 -D/hsm1 ( 1999): mS1.exit -D/hsm1 ( 1999): mS2.enter -D/hsm1 ( 1999): mS2.processMessage what=2 -D/hsm1 ( 1999): mS2.processMessage what=3 -D/hsm1 ( 1999): mS2.exit -D/hsm1 ( 1999): mP1.exit -D/hsm1 ( 1999): mP2.enter -D/hsm1 ( 1999): mP2.processMessage what=3 -D/hsm1 ( 1999): mP2.processMessage what=4 -D/hsm1 ( 1999): mP2.processMessage what=5 -D/hsm1 ( 1999): mP2.exit -D/hsm1 ( 1999): halting -</pre> - */ -public class StateMachine { - // Name of the state machine and used as logging tag - private String mName; - - /** Message.what value when quitting */ - private static final int SM_QUIT_CMD = -1; - - /** Message.what value when initializing */ - private static final int SM_INIT_CMD = -2; - - /** - * Convenience constant that maybe returned by processMessage - * to indicate the message was processed and is not to be - * processed by parent states - */ - public static final boolean HANDLED = true; - - /** - * Convenience constant that maybe returned by processMessage - * to indicate the message was NOT processed and is to be - * processed by parent states - */ - public static final boolean NOT_HANDLED = false; - - /** - * StateMachine logging record. - * {@hide} - */ - public static class LogRec { - private StateMachine mSm; - private long mTime; - private int mWhat; - private String mInfo; - private IState mState; - private IState mOrgState; - private IState mDstState; - - /** - * Constructor - * - * @param msg - * @param state the state which handled the message - * @param orgState is the first state the received the message but - * did not processes the message. - * @param transToState is the state that was transitioned to after the message was - * processed. - */ - LogRec(StateMachine sm, Message msg, String info, IState state, IState orgState, - IState transToState) { - update(sm, msg, info, state, orgState, transToState); - } - - /** - * Update the information in the record. - * @param state that handled the message - * @param orgState is the first state the received the message - * @param dstState is the state that was the transition target when logging - */ - public void update(StateMachine sm, Message msg, String info, IState state, IState orgState, - IState dstState) { - mSm = sm; - mTime = System.currentTimeMillis(); - mWhat = (msg != null) ? msg.what : 0; - mInfo = info; - mState = state; - mOrgState = orgState; - mDstState = dstState; - } - - /** - * @return time stamp - */ - public long getTime() { - return mTime; - } - - /** - * @return msg.what - */ - public long getWhat() { - return mWhat; - } - - /** - * @return the command that was executing - */ - public String getInfo() { - return mInfo; - } - - /** - * @return the state that handled this message - */ - public IState getState() { - return mState; - } - - /** - * @return the state destination state if a transition is occurring or null if none. - */ - public IState getDestState() { - return mDstState; - } - - /** - * @return the original state that received the message. - */ - public IState getOriginalState() { - return mOrgState; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("time="); - Calendar c = Calendar.getInstance(); - c.setTimeInMillis(mTime); - sb.append(String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c)); - sb.append(" processed="); - sb.append(mState == null ? "<null>" : mState.getName()); - sb.append(" org="); - sb.append(mOrgState == null ? "<null>" : mOrgState.getName()); - sb.append(" dest="); - sb.append(mDstState == null ? "<null>" : mDstState.getName()); - sb.append(" what="); - String what = mSm != null ? mSm.getWhatToString(mWhat) : ""; - if (TextUtils.isEmpty(what)) { - sb.append(mWhat); - sb.append("(0x"); - sb.append(Integer.toHexString(mWhat)); - sb.append(")"); - } else { - sb.append(what); - } - if (!TextUtils.isEmpty(mInfo)) { - sb.append(" "); - sb.append(mInfo); - } - return sb.toString(); - } - } - - /** - * A list of log records including messages recently processed by the state machine. - * - * The class maintains a list of log records including messages - * recently processed. The list is finite and may be set in the - * constructor or by calling setSize. The public interface also - * includes size which returns the number of recent records, - * count which is the number of records processed since the - * the last setSize, get which returns a record and - * add which adds a record. - */ - private static class LogRecords { - - private static final int DEFAULT_SIZE = 20; - - private Vector<LogRec> mLogRecVector = new Vector<LogRec>(); - private int mMaxSize = DEFAULT_SIZE; - private int mOldestIndex = 0; - private int mCount = 0; - private boolean mLogOnlyTransitions = false; - - /** - * private constructor use add - */ - private LogRecords() { - } - - /** - * Set size of messages to maintain and clears all current records. - * - * @param maxSize number of records to maintain at anyone time. - */ - synchronized void setSize(int maxSize) { - // TODO: once b/28217358 is fixed, add unit tests to verify that these variables are - // cleared after calling this method, and that subsequent calls to get() function as - // expected. - mMaxSize = maxSize; - mOldestIndex = 0; - mCount = 0; - mLogRecVector.clear(); - } - - synchronized void setLogOnlyTransitions(boolean enable) { - mLogOnlyTransitions = enable; - } - - synchronized boolean logOnlyTransitions() { - return mLogOnlyTransitions; - } - - /** - * @return the number of recent records. - */ - synchronized int size() { - return mLogRecVector.size(); - } - - /** - * @return the total number of records processed since size was set. - */ - synchronized int count() { - return mCount; - } - - /** - * Clear the list of records. - */ - synchronized void cleanup() { - mLogRecVector.clear(); - } - - /** - * @return the information on a particular record. 0 is the oldest - * record and size()-1 is the newest record. If the index is to - * large null is returned. - */ - synchronized LogRec get(int index) { - int nextIndex = mOldestIndex + index; - if (nextIndex >= mMaxSize) { - nextIndex -= mMaxSize; - } - if (nextIndex >= size()) { - return null; - } else { - return mLogRecVector.get(nextIndex); - } - } - - /** - * Add a processed message. - * - * @param msg - * @param messageInfo to be stored - * @param state that handled the message - * @param orgState is the first state the received the message but - * did not processes the message. - * @param transToState is the state that was transitioned to after the message was - * processed. - * - */ - synchronized void add(StateMachine sm, Message msg, String messageInfo, IState state, - IState orgState, IState transToState) { - mCount += 1; - if (mLogRecVector.size() < mMaxSize) { - mLogRecVector.add(new LogRec(sm, msg, messageInfo, state, orgState, transToState)); - } else { - LogRec pmi = mLogRecVector.get(mOldestIndex); - mOldestIndex += 1; - if (mOldestIndex >= mMaxSize) { - mOldestIndex = 0; - } - pmi.update(sm, msg, messageInfo, state, orgState, transToState); - } - } - } - - private static class SmHandler extends Handler { - - /** true if StateMachine has quit */ - private boolean mHasQuit = false; - - /** The debug flag */ - private boolean mDbg = false; - - /** The SmHandler object, identifies that message is internal */ - private static final Object mSmHandlerObj = new Object(); - - /** The current message */ - private Message mMsg; - - /** A list of log records including messages this state machine has processed */ - private LogRecords mLogRecords = new LogRecords(); - - /** true if construction of the state machine has not been completed */ - private boolean mIsConstructionCompleted; - - /** Stack used to manage the current hierarchy of states */ - private StateInfo mStateStack[]; - - /** Top of mStateStack */ - private int mStateStackTopIndex = -1; - - /** A temporary stack used to manage the state stack */ - private StateInfo mTempStateStack[]; - - /** The top of the mTempStateStack */ - private int mTempStateStackCount; - - /** State used when state machine is halted */ - private HaltingState mHaltingState = new HaltingState(); - - /** State used when state machine is quitting */ - private QuittingState mQuittingState = new QuittingState(); - - /** Reference to the StateMachine */ - private StateMachine mSm; - - /** - * Information about a state. - * Used to maintain the hierarchy. - */ - private class StateInfo { - /** The state */ - State state; - - /** The parent of this state, null if there is no parent */ - StateInfo parentStateInfo; - - /** True when the state has been entered and on the stack */ - boolean active; - - /** - * Convert StateInfo to string - */ - @Override - public String toString() { - return "state=" + state.getName() + ",active=" + active + ",parent=" - + ((parentStateInfo == null) ? "null" : parentStateInfo.state.getName()); - } - } - - /** The map of all of the states in the state machine */ - private HashMap<State, StateInfo> mStateInfo = new HashMap<State, StateInfo>(); - - /** The initial state that will process the first message */ - private State mInitialState; - - /** The destination state when transitionTo has been invoked */ - private State mDestState; - - /** - * Indicates if a transition is in progress - * - * This will be true for all calls of State.exit and all calls of State.enter except for the - * last enter call for the current destination state. - */ - private boolean mTransitionInProgress = false; - - /** The list of deferred messages */ - private ArrayList<Message> mDeferredMessages = new ArrayList<Message>(); - - /** - * State entered when transitionToHaltingState is called. - */ - private class HaltingState extends State { - @Override - public boolean processMessage(Message msg) { - mSm.haltedProcessMessage(msg); - return true; - } - } - - /** - * State entered when a valid quit message is handled. - */ - private class QuittingState extends State { - @Override - public boolean processMessage(Message msg) { - return NOT_HANDLED; - } - } - - /** - * Handle messages sent to the state machine by calling - * the current state's processMessage. It also handles - * the enter/exit calls and placing any deferred messages - * back onto the queue when transitioning to a new state. - */ - @Override - public final void handleMessage(Message msg) { - if (!mHasQuit) { - if (mSm != null && msg.what != SM_INIT_CMD && msg.what != SM_QUIT_CMD) { - mSm.onPreHandleMessage(msg); - } - - if (mDbg) mSm.log("handleMessage: E msg.what=" + msg.what); - - /** Save the current message */ - mMsg = msg; - - /** State that processed the message */ - State msgProcessedState = null; - if (mIsConstructionCompleted || (mMsg.what == SM_QUIT_CMD)) { - /** Normal path */ - msgProcessedState = processMsg(msg); - } else if (!mIsConstructionCompleted && (mMsg.what == SM_INIT_CMD) - && (mMsg.obj == mSmHandlerObj)) { - /** Initial one time path. */ - mIsConstructionCompleted = true; - invokeEnterMethods(0); - } else { - throw new RuntimeException("StateMachine.handleMessage: " - + "The start method not called, received msg: " + msg); - } - performTransitions(msgProcessedState, msg); - - // We need to check if mSm == null here as we could be quitting. - if (mDbg && mSm != null) mSm.log("handleMessage: X"); - - if (mSm != null && msg.what != SM_INIT_CMD && msg.what != SM_QUIT_CMD) { - mSm.onPostHandleMessage(msg); - } - } - } - - /** - * Do any transitions - * @param msgProcessedState is the state that processed the message - */ - private void performTransitions(State msgProcessedState, Message msg) { - /** - * If transitionTo has been called, exit and then enter - * the appropriate states. We loop on this to allow - * enter and exit methods to use transitionTo. - */ - State orgState = mStateStack[mStateStackTopIndex].state; - - /** - * Record whether message needs to be logged before we transition and - * and we won't log special messages SM_INIT_CMD or SM_QUIT_CMD which - * always set msg.obj to the handler. - */ - boolean recordLogMsg = mSm.recordLogRec(mMsg) && (msg.obj != mSmHandlerObj); - - if (mLogRecords.logOnlyTransitions()) { - /** Record only if there is a transition */ - if (mDestState != null) { - mLogRecords.add(mSm, mMsg, mSm.getLogRecString(mMsg), msgProcessedState, - orgState, mDestState); - } - } else if (recordLogMsg) { - /** Record message */ - mLogRecords.add(mSm, mMsg, mSm.getLogRecString(mMsg), msgProcessedState, orgState, - mDestState); - } - - State destState = mDestState; - if (destState != null) { - /** - * Process the transitions including transitions in the enter/exit methods - */ - while (true) { - if (mDbg) mSm.log("handleMessage: new destination call exit/enter"); - - /** - * Determine the states to exit and enter and return the - * common ancestor state of the enter/exit states. Then - * invoke the exit methods then the enter methods. - */ - StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState); - // flag is cleared in invokeEnterMethods before entering the target state - mTransitionInProgress = true; - invokeExitMethods(commonStateInfo); - int stateStackEnteringIndex = moveTempStateStackToStateStack(); - invokeEnterMethods(stateStackEnteringIndex); - - /** - * Since we have transitioned to a new state we need to have - * any deferred messages moved to the front of the message queue - * so they will be processed before any other messages in the - * message queue. - */ - moveDeferredMessageAtFrontOfQueue(); - - if (destState != mDestState) { - // A new mDestState so continue looping - destState = mDestState; - } else { - // No change in mDestState so we're done - break; - } - } - mDestState = null; - } - - /** - * After processing all transitions check and - * see if the last transition was to quit or halt. - */ - if (destState != null) { - if (destState == mQuittingState) { - /** - * Call onQuitting to let subclasses cleanup. - */ - mSm.onQuitting(); - cleanupAfterQuitting(); - } else if (destState == mHaltingState) { - /** - * Call onHalting() if we've transitioned to the halting - * state. All subsequent messages will be processed in - * in the halting state which invokes haltedProcessMessage(msg); - */ - mSm.onHalting(); - } - } - } - - /** - * Cleanup all the static variables and the looper after the SM has been quit. - */ - private final void cleanupAfterQuitting() { - if (mSm.mSmThread != null) { - // If we made the thread then quit looper which stops the thread. - getLooper().quit(); - mSm.mSmThread = null; - } - - mSm.mSmHandler = null; - mSm = null; - mMsg = null; - mLogRecords.cleanup(); - mStateStack = null; - mTempStateStack = null; - mStateInfo.clear(); - mInitialState = null; - mDestState = null; - mDeferredMessages.clear(); - mHasQuit = true; - } - - /** - * Complete the construction of the state machine. - */ - private final void completeConstruction() { - if (mDbg) mSm.log("completeConstruction: E"); - - /** - * Determine the maximum depth of the state hierarchy - * so we can allocate the state stacks. - */ - int maxDepth = 0; - for (StateInfo si : mStateInfo.values()) { - int depth = 0; - for (StateInfo i = si; i != null; depth++) { - i = i.parentStateInfo; - } - if (maxDepth < depth) { - maxDepth = depth; - } - } - if (mDbg) mSm.log("completeConstruction: maxDepth=" + maxDepth); - - mStateStack = new StateInfo[maxDepth]; - mTempStateStack = new StateInfo[maxDepth]; - setupInitialStateStack(); - - /** Sending SM_INIT_CMD message to invoke enter methods asynchronously */ - sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj)); - - if (mDbg) mSm.log("completeConstruction: X"); - } - - /** - * Process the message. If the current state doesn't handle - * it, call the states parent and so on. If it is never handled then - * call the state machines unhandledMessage method. - * @return the state that processed the message - */ - private final State processMsg(Message msg) { - StateInfo curStateInfo = mStateStack[mStateStackTopIndex]; - if (mDbg) { - mSm.log("processMsg: " + curStateInfo.state.getName()); - } - - if (isQuit(msg)) { - transitionTo(mQuittingState); - } else { - while (!curStateInfo.state.processMessage(msg)) { - /** - * Not processed - */ - curStateInfo = curStateInfo.parentStateInfo; - if (curStateInfo == null) { - /** - * No parents left so it's not handled - */ - mSm.unhandledMessage(msg); - break; - } - if (mDbg) { - mSm.log("processMsg: " + curStateInfo.state.getName()); - } - } - } - return (curStateInfo != null) ? curStateInfo.state : null; - } - - /** - * Call the exit method for each state from the top of stack - * up to the common ancestor state. - */ - private final void invokeExitMethods(StateInfo commonStateInfo) { - while ((mStateStackTopIndex >= 0) - && (mStateStack[mStateStackTopIndex] != commonStateInfo)) { - State curState = mStateStack[mStateStackTopIndex].state; - if (mDbg) mSm.log("invokeExitMethods: " + curState.getName()); - curState.exit(); - mStateStack[mStateStackTopIndex].active = false; - mStateStackTopIndex -= 1; - } - } - - /** - * Invoke the enter method starting at the entering index to top of state stack - */ - private final void invokeEnterMethods(int stateStackEnteringIndex) { - for (int i = stateStackEnteringIndex; i <= mStateStackTopIndex; i++) { - if (stateStackEnteringIndex == mStateStackTopIndex) { - // Last enter state for transition - mTransitionInProgress = false; - } - if (mDbg) mSm.log("invokeEnterMethods: " + mStateStack[i].state.getName()); - mStateStack[i].state.enter(); - mStateStack[i].active = true; - } - mTransitionInProgress = false; // ensure flag set to false if no methods called - } - - /** - * Move the deferred message to the front of the message queue. - */ - private final void moveDeferredMessageAtFrontOfQueue() { - /** - * The oldest messages on the deferred list must be at - * the front of the queue so start at the back, which - * as the most resent message and end with the oldest - * messages at the front of the queue. - */ - for (int i = mDeferredMessages.size() - 1; i >= 0; i--) { - Message curMsg = mDeferredMessages.get(i); - if (mDbg) mSm.log("moveDeferredMessageAtFrontOfQueue; what=" + curMsg.what); - sendMessageAtFrontOfQueue(curMsg); - } - mDeferredMessages.clear(); - } - - /** - * Move the contents of the temporary stack to the state stack - * reversing the order of the items on the temporary stack as - * they are moved. - * - * @return index into mStateStack where entering needs to start - */ - private final int moveTempStateStackToStateStack() { - int startingIndex = mStateStackTopIndex + 1; - int i = mTempStateStackCount - 1; - int j = startingIndex; - while (i >= 0) { - if (mDbg) mSm.log("moveTempStackToStateStack: i=" + i + ",j=" + j); - mStateStack[j] = mTempStateStack[i]; - j += 1; - i -= 1; - } - - mStateStackTopIndex = j - 1; - if (mDbg) { - mSm.log("moveTempStackToStateStack: X mStateStackTop=" + mStateStackTopIndex - + ",startingIndex=" + startingIndex + ",Top=" - + mStateStack[mStateStackTopIndex].state.getName()); - } - return startingIndex; - } - - /** - * Setup the mTempStateStack with the states we are going to enter. - * - * This is found by searching up the destState's ancestors for a - * state that is already active i.e. StateInfo.active == true. - * The destStae and all of its inactive parents will be on the - * TempStateStack as the list of states to enter. - * - * @return StateInfo of the common ancestor for the destState and - * current state or null if there is no common parent. - */ - private final StateInfo setupTempStateStackWithStatesToEnter(State destState) { - /** - * Search up the parent list of the destination state for an active - * state. Use a do while() loop as the destState must always be entered - * even if it is active. This can happen if we are exiting/entering - * the current state. - */ - mTempStateStackCount = 0; - StateInfo curStateInfo = mStateInfo.get(destState); - do { - mTempStateStack[mTempStateStackCount++] = curStateInfo; - curStateInfo = curStateInfo.parentStateInfo; - } while ((curStateInfo != null) && !curStateInfo.active); - - if (mDbg) { - mSm.log("setupTempStateStackWithStatesToEnter: X mTempStateStackCount=" - + mTempStateStackCount + ",curStateInfo: " + curStateInfo); - } - return curStateInfo; - } - - /** - * Initialize StateStack to mInitialState. - */ - private final void setupInitialStateStack() { - if (mDbg) { - mSm.log("setupInitialStateStack: E mInitialState=" + mInitialState.getName()); - } - - StateInfo curStateInfo = mStateInfo.get(mInitialState); - for (mTempStateStackCount = 0; curStateInfo != null; mTempStateStackCount++) { - mTempStateStack[mTempStateStackCount] = curStateInfo; - curStateInfo = curStateInfo.parentStateInfo; - } - - // Empty the StateStack - mStateStackTopIndex = -1; - - moveTempStateStackToStateStack(); - } - - /** - * @return current message - */ - private final Message getCurrentMessage() { - return mMsg; - } - - /** - * @return current state - */ - private final IState getCurrentState() { - return mStateStack[mStateStackTopIndex].state; - } - - /** - * Add a new state to the state machine. Bottom up addition - * of states is allowed but the same state may only exist - * in one hierarchy. - * - * @param state the state to add - * @param parent the parent of state - * @return stateInfo for this state - */ - private final StateInfo addState(State state, State parent) { - if (mDbg) { - mSm.log("addStateInternal: E state=" + state.getName() + ",parent=" - + ((parent == null) ? "" : parent.getName())); - } - StateInfo parentStateInfo = null; - if (parent != null) { - parentStateInfo = mStateInfo.get(parent); - if (parentStateInfo == null) { - // Recursively add our parent as it's not been added yet. - parentStateInfo = addState(parent, null); - } - } - StateInfo stateInfo = mStateInfo.get(state); - if (stateInfo == null) { - stateInfo = new StateInfo(); - mStateInfo.put(state, stateInfo); - } - - // Validate that we aren't adding the same state in two different hierarchies. - if ((stateInfo.parentStateInfo != null) - && (stateInfo.parentStateInfo != parentStateInfo)) { - throw new RuntimeException("state already added"); - } - stateInfo.state = state; - stateInfo.parentStateInfo = parentStateInfo; - stateInfo.active = false; - if (mDbg) mSm.log("addStateInternal: X stateInfo: " + stateInfo); - return stateInfo; - } - - /** - * Remove a state from the state machine. Will not remove the state if it is currently - * active or if it has any children in the hierarchy. - * @param state the state to remove - */ - private void removeState(State state) { - StateInfo stateInfo = mStateInfo.get(state); - if (stateInfo == null || stateInfo.active) { - return; - } - boolean isParent = mStateInfo.values().stream() - .filter(si -> si.parentStateInfo == stateInfo) - .findAny() - .isPresent(); - if (isParent) { - return; - } - mStateInfo.remove(state); - } - - /** - * Constructor - * - * @param looper for dispatching messages - * @param sm the hierarchical state machine - */ - private SmHandler(Looper looper, StateMachine sm) { - super(looper); - mSm = sm; - - addState(mHaltingState, null); - addState(mQuittingState, null); - } - - /** @see StateMachine#setInitialState(State) */ - private final void setInitialState(State initialState) { - if (mDbg) mSm.log("setInitialState: initialState=" + initialState.getName()); - mInitialState = initialState; - } - - /** @see StateMachine#transitionTo(IState) */ - private final void transitionTo(IState destState) { - if (mTransitionInProgress) { - Log.wtf(mSm.mName, "transitionTo called while transition already in progress to " + - mDestState + ", new target state=" + destState); - } - mDestState = (State) destState; - if (mDbg) mSm.log("transitionTo: destState=" + mDestState.getName()); - } - - /** @see StateMachine#deferMessage(Message) */ - private final void deferMessage(Message msg) { - if (mDbg) mSm.log("deferMessage: msg=" + msg.what); - - /* Copy the "msg" to "newMsg" as "msg" will be recycled */ - Message newMsg = obtainMessage(); - newMsg.copyFrom(msg); - - mDeferredMessages.add(newMsg); - } - - /** @see StateMachine#quit() */ - private final void quit() { - if (mDbg) mSm.log("quit:"); - sendMessage(obtainMessage(SM_QUIT_CMD, mSmHandlerObj)); - } - - /** @see StateMachine#quitNow() */ - private final void quitNow() { - if (mDbg) mSm.log("quitNow:"); - sendMessageAtFrontOfQueue(obtainMessage(SM_QUIT_CMD, mSmHandlerObj)); - } - - /** Validate that the message was sent by quit or quitNow. */ - private final boolean isQuit(Message msg) { - return (msg.what == SM_QUIT_CMD) && (msg.obj == mSmHandlerObj); - } - - /** @see StateMachine#isDbg() */ - private final boolean isDbg() { - return mDbg; - } - - /** @see StateMachine#setDbg(boolean) */ - private final void setDbg(boolean dbg) { - mDbg = dbg; - } - - } - - private SmHandler mSmHandler; - private HandlerThread mSmThread; - - /** - * Initialize. - * - * @param looper for this state machine - * @param name of the state machine - */ - private void initStateMachine(String name, Looper looper) { - mName = name; - mSmHandler = new SmHandler(looper, this); - } - - /** - * Constructor creates a StateMachine with its own thread. - * - * @param name of the state machine - */ - @UnsupportedAppUsage - protected StateMachine(String name) { - mSmThread = new HandlerThread(name); - mSmThread.start(); - Looper looper = mSmThread.getLooper(); - - initStateMachine(name, looper); - } - - /** - * Constructor creates a StateMachine using the looper. - * - * @param name of the state machine - */ - @UnsupportedAppUsage - protected StateMachine(String name, Looper looper) { - initStateMachine(name, looper); - } - - /** - * Constructor creates a StateMachine using the handler. - * - * @param name of the state machine - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - protected StateMachine(String name, Handler handler) { - initStateMachine(name, handler.getLooper()); - } - - /** - * Notifies subclass that the StateMachine handler is about to process the Message msg - * @param msg The message that is being handled - */ - protected void onPreHandleMessage(Message msg) { - } - - /** - * Notifies subclass that the StateMachine handler has finished processing the Message msg and - * has possibly transitioned to a new state. - * @param msg The message that is being handled - */ - protected void onPostHandleMessage(Message msg) { - } - - /** - * Add a new state to the state machine - * @param state the state to add - * @param parent the parent of state - */ - public final void addState(State state, State parent) { - mSmHandler.addState(state, parent); - } - - /** - * Add a new state to the state machine, parent will be null - * @param state to add - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public final void addState(State state) { - mSmHandler.addState(state, null); - } - - /** - * Removes a state from the state machine, unless it is currently active or if it has children. - * @param state state to remove - */ - public final void removeState(State state) { - mSmHandler.removeState(state); - } - - /** - * Set the initial state. This must be invoked before - * and messages are sent to the state machine. - * - * @param initialState is the state which will receive the first message. - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public final void setInitialState(State initialState) { - mSmHandler.setInitialState(initialState); - } - - /** - * @return current message - */ - public final Message getCurrentMessage() { - // mSmHandler can be null if the state machine has quit. - SmHandler smh = mSmHandler; - if (smh == null) return null; - return smh.getCurrentMessage(); - } - - /** - * @return current state - */ - public final IState getCurrentState() { - // mSmHandler can be null if the state machine has quit. - SmHandler smh = mSmHandler; - if (smh == null) return null; - return smh.getCurrentState(); - } - - /** - * transition to destination state. Upon returning - * from processMessage the current state's exit will - * be executed and upon the next message arriving - * destState.enter will be invoked. - * - * this function can also be called inside the enter function of the - * previous transition target, but the behavior is undefined when it is - * called mid-way through a previous transition (for example, calling this - * in the enter() routine of a intermediate node when the current transition - * target is one of the nodes descendants). - * - * @param destState will be the state that receives the next message. - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public final void transitionTo(IState destState) { - mSmHandler.transitionTo(destState); - } - - /** - * transition to halt state. Upon returning - * from processMessage we will exit all current - * states, execute the onHalting() method and then - * for all subsequent messages haltedProcessMessage - * will be called. - */ - public final void transitionToHaltingState() { - mSmHandler.transitionTo(mSmHandler.mHaltingState); - } - - /** - * Defer this message until next state transition. - * Upon transitioning all deferred messages will be - * placed on the queue and reprocessed in the original - * order. (i.e. The next state the oldest messages will - * be processed first) - * - * @param msg is deferred until the next transition. - */ - public final void deferMessage(Message msg) { - mSmHandler.deferMessage(msg); - } - - /** - * Called when message wasn't handled - * - * @param msg that couldn't be handled. - */ - protected void unhandledMessage(Message msg) { - if (mSmHandler.mDbg) loge(" - unhandledMessage: msg.what=" + msg.what); - } - - /** - * Called for any message that is received after - * transitionToHalting is called. - */ - protected void haltedProcessMessage(Message msg) { - } - - /** - * This will be called once after handling a message that called - * transitionToHalting. All subsequent messages will invoke - * {@link StateMachine#haltedProcessMessage(Message)} - */ - protected void onHalting() { - } - - /** - * This will be called once after a quit message that was NOT handled by - * the derived StateMachine. The StateMachine will stop and any subsequent messages will be - * ignored. In addition, if this StateMachine created the thread, the thread will - * be stopped after this method returns. - */ - protected void onQuitting() { - } - - /** - * @return the name - */ - public final String getName() { - return mName; - } - - /** - * Set number of log records to maintain and clears all current records. - * - * @param maxSize number of messages to maintain at anyone time. - */ - public final void setLogRecSize(int maxSize) { - mSmHandler.mLogRecords.setSize(maxSize); - } - - /** - * Set to log only messages that cause a state transition - * - * @param enable {@code true} to enable, {@code false} to disable - */ - public final void setLogOnlyTransitions(boolean enable) { - mSmHandler.mLogRecords.setLogOnlyTransitions(enable); - } - - /** - * @return the number of log records currently readable - */ - public final int getLogRecSize() { - // mSmHandler can be null if the state machine has quit. - SmHandler smh = mSmHandler; - if (smh == null) return 0; - return smh.mLogRecords.size(); - } - - /** - * @return the number of log records we can store - */ - @VisibleForTesting - public final int getLogRecMaxSize() { - // mSmHandler can be null if the state machine has quit. - SmHandler smh = mSmHandler; - if (smh == null) return 0; - return smh.mLogRecords.mMaxSize; - } - - /** - * @return the total number of records processed - */ - public final int getLogRecCount() { - // mSmHandler can be null if the state machine has quit. - SmHandler smh = mSmHandler; - if (smh == null) return 0; - return smh.mLogRecords.count(); - } - - /** - * @return a log record, or null if index is out of range - */ - public final LogRec getLogRec(int index) { - // mSmHandler can be null if the state machine has quit. - SmHandler smh = mSmHandler; - if (smh == null) return null; - return smh.mLogRecords.get(index); - } - - /** - * @return a copy of LogRecs as a collection - */ - public final Collection<LogRec> copyLogRecs() { - Vector<LogRec> vlr = new Vector<LogRec>(); - SmHandler smh = mSmHandler; - if (smh != null) { - for (LogRec lr : smh.mLogRecords.mLogRecVector) { - vlr.add(lr); - } - } - return vlr; - } - - /** - * Add the string to LogRecords. - * - * @param string - */ - public void addLogRec(String string) { - // mSmHandler can be null if the state machine has quit. - SmHandler smh = mSmHandler; - if (smh == null) return; - smh.mLogRecords.add(this, smh.getCurrentMessage(), string, smh.getCurrentState(), - smh.mStateStack[smh.mStateStackTopIndex].state, smh.mDestState); - } - - /** - * @return true if msg should be saved in the log, default is true. - */ - protected boolean recordLogRec(Message msg) { - return true; - } - - /** - * Return a string to be logged by LogRec, default - * is an empty string. Override if additional information is desired. - * - * @param msg that was processed - * @return information to be logged as a String - */ - protected String getLogRecString(Message msg) { - return ""; - } - - /** - * @return the string for msg.what - */ - protected String getWhatToString(int what) { - return null; - } - - /** - * @return Handler, maybe null if state machine has quit. - */ - public final Handler getHandler() { - return mSmHandler; - } - - /** - * Get a message and set Message.target state machine handler. - * - * Note: The handler can be null if the state machine has quit, - * which means target will be null and may cause a AndroidRuntimeException - * in MessageQueue#enqueMessage if sent directly or if sent using - * StateMachine#sendMessage the message will just be ignored. - * - * @return A Message object from the global pool - */ - public final Message obtainMessage() { - return Message.obtain(mSmHandler); - } - - /** - * Get a message and set Message.target state machine handler, what. - * - * Note: The handler can be null if the state machine has quit, - * which means target will be null and may cause a AndroidRuntimeException - * in MessageQueue#enqueMessage if sent directly or if sent using - * StateMachine#sendMessage the message will just be ignored. - * - * @param what is the assigned to Message.what. - * @return A Message object from the global pool - */ - public final Message obtainMessage(int what) { - return Message.obtain(mSmHandler, what); - } - - /** - * Get a message and set Message.target state machine handler, - * what and obj. - * - * Note: The handler can be null if the state machine has quit, - * which means target will be null and may cause a AndroidRuntimeException - * in MessageQueue#enqueMessage if sent directly or if sent using - * StateMachine#sendMessage the message will just be ignored. - * - * @param what is the assigned to Message.what. - * @param obj is assigned to Message.obj. - * @return A Message object from the global pool - */ - public final Message obtainMessage(int what, Object obj) { - return Message.obtain(mSmHandler, what, obj); - } - - /** - * Get a message and set Message.target state machine handler, - * what, arg1 and arg2 - * - * Note: The handler can be null if the state machine has quit, - * which means target will be null and may cause a AndroidRuntimeException - * in MessageQueue#enqueMessage if sent directly or if sent using - * StateMachine#sendMessage the message will just be ignored. - * - * @param what is assigned to Message.what - * @param arg1 is assigned to Message.arg1 - * @return A Message object from the global pool - */ - public final Message obtainMessage(int what, int arg1) { - // use this obtain so we don't match the obtain(h, what, Object) method - return Message.obtain(mSmHandler, what, arg1, 0); - } - - /** - * Get a message and set Message.target state machine handler, - * what, arg1 and arg2 - * - * Note: The handler can be null if the state machine has quit, - * which means target will be null and may cause a AndroidRuntimeException - * in MessageQueue#enqueMessage if sent directly or if sent using - * StateMachine#sendMessage the message will just be ignored. - * - * @param what is assigned to Message.what - * @param arg1 is assigned to Message.arg1 - * @param arg2 is assigned to Message.arg2 - * @return A Message object from the global pool - */ - @UnsupportedAppUsage - public final Message obtainMessage(int what, int arg1, int arg2) { - return Message.obtain(mSmHandler, what, arg1, arg2); - } - - /** - * Get a message and set Message.target state machine handler, - * what, arg1, arg2 and obj - * - * Note: The handler can be null if the state machine has quit, - * which means target will be null and may cause a AndroidRuntimeException - * in MessageQueue#enqueMessage if sent directly or if sent using - * StateMachine#sendMessage the message will just be ignored. - * - * @param what is assigned to Message.what - * @param arg1 is assigned to Message.arg1 - * @param arg2 is assigned to Message.arg2 - * @param obj is assigned to Message.obj - * @return A Message object from the global pool - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public final Message obtainMessage(int what, int arg1, int arg2, Object obj) { - return Message.obtain(mSmHandler, what, arg1, arg2, obj); - } - - /** - * Enqueue a message to this state machine. - * - * Message is ignored if state machine has quit. - */ - @UnsupportedAppUsage - public void sendMessage(int what) { - // mSmHandler can be null if the state machine has quit. - SmHandler smh = mSmHandler; - if (smh == null) return; - - smh.sendMessage(obtainMessage(what)); - } - - /** - * Enqueue a message to this state machine. - * - * Message is ignored if state machine has quit. - */ - @UnsupportedAppUsage - public void sendMessage(int what, Object obj) { - // mSmHandler can be null if the state machine has quit. - SmHandler smh = mSmHandler; - if (smh == null) return; - - smh.sendMessage(obtainMessage(what, obj)); - } - - /** - * Enqueue a message to this state machine. - * - * Message is ignored if state machine has quit. - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public void sendMessage(int what, int arg1) { - // mSmHandler can be null if the state machine has quit. - SmHandler smh = mSmHandler; - if (smh == null) return; - - smh.sendMessage(obtainMessage(what, arg1)); - } - - /** - * Enqueue a message to this state machine. - * - * Message is ignored if state machine has quit. - */ - public void sendMessage(int what, int arg1, int arg2) { - // mSmHandler can be null if the state machine has quit. - SmHandler smh = mSmHandler; - if (smh == null) return; - - smh.sendMessage(obtainMessage(what, arg1, arg2)); - } - - /** - * Enqueue a message to this state machine. - * - * Message is ignored if state machine has quit. - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public void sendMessage(int what, int arg1, int arg2, Object obj) { - // mSmHandler can be null if the state machine has quit. - SmHandler smh = mSmHandler; - if (smh == null) return; - - smh.sendMessage(obtainMessage(what, arg1, arg2, obj)); - } - - /** - * Enqueue a message to this state machine. - * - * Message is ignored if state machine has quit. - */ - @UnsupportedAppUsage - public void sendMessage(Message msg) { - // mSmHandler can be null if the state machine has quit. - SmHandler smh = mSmHandler; - if (smh == null) return; - - smh.sendMessage(msg); - } - - /** - * Enqueue a message to this state machine after a delay. - * - * Message is ignored if state machine has quit. - */ - public void sendMessageDelayed(int what, long delayMillis) { - // mSmHandler can be null if the state machine has quit. - SmHandler smh = mSmHandler; - if (smh == null) return; - - smh.sendMessageDelayed(obtainMessage(what), delayMillis); - } - - /** - * Enqueue a message to this state machine after a delay. - * - * Message is ignored if state machine has quit. - */ - public void sendMessageDelayed(int what, Object obj, long delayMillis) { - // mSmHandler can be null if the state machine has quit. - SmHandler smh = mSmHandler; - if (smh == null) return; - - smh.sendMessageDelayed(obtainMessage(what, obj), delayMillis); - } - - /** - * Enqueue a message to this state machine after a delay. - * - * Message is ignored if state machine has quit. - */ - public void sendMessageDelayed(int what, int arg1, long delayMillis) { - // mSmHandler can be null if the state machine has quit. - SmHandler smh = mSmHandler; - if (smh == null) return; - - smh.sendMessageDelayed(obtainMessage(what, arg1), delayMillis); - } - - /** - * Enqueue a message to this state machine after a delay. - * - * Message is ignored if state machine has quit. - */ - public void sendMessageDelayed(int what, int arg1, int arg2, long delayMillis) { - // mSmHandler can be null if the state machine has quit. - SmHandler smh = mSmHandler; - if (smh == null) return; - - smh.sendMessageDelayed(obtainMessage(what, arg1, arg2), delayMillis); - } - - /** - * Enqueue a message to this state machine after a delay. - * - * Message is ignored if state machine has quit. - */ - public void sendMessageDelayed(int what, int arg1, int arg2, Object obj, - long delayMillis) { - // mSmHandler can be null if the state machine has quit. - SmHandler smh = mSmHandler; - if (smh == null) return; - - smh.sendMessageDelayed(obtainMessage(what, arg1, arg2, obj), delayMillis); - } - - /** - * Enqueue a message to this state machine after a delay. - * - * Message is ignored if state machine has quit. - */ - public void sendMessageDelayed(Message msg, long delayMillis) { - // mSmHandler can be null if the state machine has quit. - SmHandler smh = mSmHandler; - if (smh == null) return; - - smh.sendMessageDelayed(msg, delayMillis); - } - - /** - * Enqueue a message to the front of the queue for this state machine. - * Protected, may only be called by instances of StateMachine. - * - * Message is ignored if state machine has quit. - */ - protected final void sendMessageAtFrontOfQueue(int what) { - // mSmHandler can be null if the state machine has quit. - SmHandler smh = mSmHandler; - if (smh == null) return; - - smh.sendMessageAtFrontOfQueue(obtainMessage(what)); - } - - /** - * Enqueue a message to the front of the queue for this state machine. - * Protected, may only be called by instances of StateMachine. - * - * Message is ignored if state machine has quit. - */ - protected final void sendMessageAtFrontOfQueue(int what, Object obj) { - // mSmHandler can be null if the state machine has quit. - SmHandler smh = mSmHandler; - if (smh == null) return; - - smh.sendMessageAtFrontOfQueue(obtainMessage(what, obj)); - } - - /** - * Enqueue a message to the front of the queue for this state machine. - * Protected, may only be called by instances of StateMachine. - * - * Message is ignored if state machine has quit. - */ - protected final void sendMessageAtFrontOfQueue(int what, int arg1) { - // mSmHandler can be null if the state machine has quit. - SmHandler smh = mSmHandler; - if (smh == null) return; - - smh.sendMessageAtFrontOfQueue(obtainMessage(what, arg1)); - } - - - /** - * Enqueue a message to the front of the queue for this state machine. - * Protected, may only be called by instances of StateMachine. - * - * Message is ignored if state machine has quit. - */ - protected final void sendMessageAtFrontOfQueue(int what, int arg1, int arg2) { - // mSmHandler can be null if the state machine has quit. - SmHandler smh = mSmHandler; - if (smh == null) return; - - smh.sendMessageAtFrontOfQueue(obtainMessage(what, arg1, arg2)); - } - - /** - * Enqueue a message to the front of the queue for this state machine. - * Protected, may only be called by instances of StateMachine. - * - * Message is ignored if state machine has quit. - */ - protected final void sendMessageAtFrontOfQueue(int what, int arg1, int arg2, Object obj) { - // mSmHandler can be null if the state machine has quit. - SmHandler smh = mSmHandler; - if (smh == null) return; - - smh.sendMessageAtFrontOfQueue(obtainMessage(what, arg1, arg2, obj)); - } - - /** - * Enqueue a message to the front of the queue for this state machine. - * Protected, may only be called by instances of StateMachine. - * - * Message is ignored if state machine has quit. - */ - protected final void sendMessageAtFrontOfQueue(Message msg) { - // mSmHandler can be null if the state machine has quit. - SmHandler smh = mSmHandler; - if (smh == null) return; - - smh.sendMessageAtFrontOfQueue(msg); - } - - /** - * Removes a message from the message queue. - * Protected, may only be called by instances of StateMachine. - */ - protected final void removeMessages(int what) { - // mSmHandler can be null if the state machine has quit. - SmHandler smh = mSmHandler; - if (smh == null) return; - - smh.removeMessages(what); - } - - /** - * Removes a message from the deferred messages queue. - */ - protected final void removeDeferredMessages(int what) { - SmHandler smh = mSmHandler; - if (smh == null) return; - - Iterator<Message> iterator = smh.mDeferredMessages.iterator(); - while (iterator.hasNext()) { - Message msg = iterator.next(); - if (msg.what == what) iterator.remove(); - } - } - - /** - * Check if there are any pending messages with code 'what' in deferred messages queue. - */ - protected final boolean hasDeferredMessages(int what) { - SmHandler smh = mSmHandler; - if (smh == null) return false; - - Iterator<Message> iterator = smh.mDeferredMessages.iterator(); - while (iterator.hasNext()) { - Message msg = iterator.next(); - if (msg.what == what) return true; - } - - return false; - } - - /** - * Check if there are any pending posts of messages with code 'what' in - * the message queue. This does NOT check messages in deferred message queue. - */ - protected final boolean hasMessages(int what) { - SmHandler smh = mSmHandler; - if (smh == null) return false; - - return smh.hasMessages(what); - } - - /** - * Validate that the message was sent by - * {@link StateMachine#quit} or {@link StateMachine#quitNow}. - * */ - protected final boolean isQuit(Message msg) { - // mSmHandler can be null if the state machine has quit. - SmHandler smh = mSmHandler; - if (smh == null) return msg.what == SM_QUIT_CMD; - - return smh.isQuit(msg); - } - - /** - * Quit the state machine after all currently queued up messages are processed. - */ - public final void quit() { - // mSmHandler can be null if the state machine is already stopped. - SmHandler smh = mSmHandler; - if (smh == null) return; - - smh.quit(); - } - - /** - * Quit the state machine immediately all currently queued messages will be discarded. - */ - public final void quitNow() { - // mSmHandler can be null if the state machine is already stopped. - SmHandler smh = mSmHandler; - if (smh == null) return; - - smh.quitNow(); - } - - /** - * @return if debugging is enabled - */ - public boolean isDbg() { - // mSmHandler can be null if the state machine has quit. - SmHandler smh = mSmHandler; - if (smh == null) return false; - - return smh.isDbg(); - } - - /** - * Set debug enable/disabled. - * - * @param dbg is true to enable debugging. - */ - public void setDbg(boolean dbg) { - // mSmHandler can be null if the state machine has quit. - SmHandler smh = mSmHandler; - if (smh == null) return; - - smh.setDbg(dbg); - } - - /** - * Start the state machine. - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public void start() { - // mSmHandler can be null if the state machine has quit. - SmHandler smh = mSmHandler; - if (smh == null) return; - - /** Send the complete construction message */ - smh.completeConstruction(); - } - - /** - * Dump the current state. - * - * @param fd - * @param pw - * @param args - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println(getName() + ":"); - pw.println(" total records=" + getLogRecCount()); - for (int i = 0; i < getLogRecSize(); i++) { - pw.println(" rec[" + i + "]: " + getLogRec(i)); - pw.flush(); - } - final IState curState = getCurrentState(); - pw.println("curState=" + (curState == null ? "<QUIT>" : curState.getName())); - } - - @Override - public String toString() { - String name = "(null)"; - String state = "(null)"; - try { - name = mName.toString(); - state = mSmHandler.getCurrentState().getName().toString(); - } catch (NullPointerException | ArrayIndexOutOfBoundsException e) { - // Will use default(s) initialized above. - } - return "name=" + name + " state=" + state; - } - - /** - * Log with debug and add to the LogRecords. - * - * @param s is string log - */ - protected void logAndAddLogRec(String s) { - addLogRec(s); - log(s); - } - - /** - * Log with debug - * - * @param s is string log - */ - protected void log(String s) { - Log.d(mName, s); - } - - /** - * Log with debug attribute - * - * @param s is string log - */ - protected void logd(String s) { - Log.d(mName, s); - } - - /** - * Log with verbose attribute - * - * @param s is string log - */ - protected void logv(String s) { - Log.v(mName, s); - } - - /** - * Log with info attribute - * - * @param s is string log - */ - protected void logi(String s) { - Log.i(mName, s); - } - - /** - * Log with warning attribute - * - * @param s is string log - */ - protected void logw(String s) { - Log.w(mName, s); - } - - /** - * Log with error attribute - * - * @param s is string log - */ - protected void loge(String s) { - Log.e(mName, s); - } - - /** - * Log with error attribute - * - * @param s is string log - * @param e is a Throwable which logs additional information. - */ - protected void loge(String s, Throwable e) { - Log.e(mName, s, e); - } -} diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index db0cc277c3af..74fb5431d77c 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -20,23 +20,11 @@ <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <!-- Suffix added to a number to signify size in bytes. --> <string name="byteShort">B</string> - <!-- Suffix added to a number to signify size in kilobytes (1000 bytes). - If you retain the Latin script for the localization, please use the lowercase - 'k', as it signifies 1000 bytes as opposed to 1024 bytes. --> - <string name="kilobyteShort">kB</string> - <!-- Suffix added to a number to signify size in megabytes. --> - <string name="megabyteShort">MB</string> - <!-- Suffix added to a number to signify size in gigabytes. --> - <string name="gigabyteShort">GB</string> - <!-- Suffix added to a number to signify size in terabytes. --> - <string name="terabyteShort">TB</string> - <!-- Suffix added to a number to signify size in petabytes. --> - <string name="petabyteShort">PB</string> - <!-- Format string used to add a suffix like "kB" or "MB" to a number - to display a size in kilobytes, megabytes, or other size units. - Some languages (like French) will want to add a space between - the placeholders. --> - <string name="fileSizeSuffix"><xliff:g id="number" example="123">%1$s</xliff:g> <xliff:g id="unit" example="MB">%2$s</xliff:g></string> + <!-- Format string used to add a suffix like "B" to a number + to display a size in bytes. + Some languages may want to remove the space between the placeholders + or replace it with a non-breaking space. --> + <string name="fileSizeSuffix"><xliff:g id="number" example="123">%1$s</xliff:g> <xliff:g id="unit" example="B">%2$s</xliff:g></string> <!-- Used in Contacts for a field that has no label and in Note Pad for a note with no name. --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index c4bb65761482..5894bdc389dc 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -737,7 +737,6 @@ <java-symbol type="string" name="fileSizeSuffix" /> <java-symbol type="string" name="force_close" /> <java-symbol type="string" name="gadget_host_error_inflating" /> - <java-symbol type="string" name="gigabyteShort" /> <java-symbol type="string" name="gpsNotifMessage" /> <java-symbol type="string" name="gpsNotifTicker" /> <java-symbol type="string" name="gpsNotifTitle" /> @@ -793,7 +792,6 @@ <java-symbol type="string" name="keyboardview_keycode_enter" /> <java-symbol type="string" name="keyboardview_keycode_mode_change" /> <java-symbol type="string" name="keyboardview_keycode_shift" /> - <java-symbol type="string" name="kilobyteShort" /> <java-symbol type="string" name="last_month" /> <java-symbol type="string" name="launchBrowserDefault" /> <java-symbol type="string" name="lock_to_app_unlock_pin" /> @@ -810,7 +808,6 @@ <java-symbol type="string" name="lockscreen_emergency_call" /> <java-symbol type="string" name="lockscreen_return_to_call" /> <java-symbol type="string" name="low_memory" /> - <java-symbol type="string" name="megabyteShort" /> <java-symbol type="string" name="midnight" /> <java-symbol type="string" name="mismatchPin" /> <java-symbol type="string" name="mmiComplete" /> @@ -840,7 +837,6 @@ <java-symbol type="string" name="passwordIncorrect" /> <java-symbol type="string" name="perms_description_app" /> <java-symbol type="string" name="perms_new_perm_prefix" /> - <java-symbol type="string" name="petabyteShort" /> <java-symbol type="string" name="peerTtyModeFull" /> <java-symbol type="string" name="peerTtyModeHco" /> <java-symbol type="string" name="peerTtyModeVco" /> @@ -1016,7 +1012,6 @@ <java-symbol type="string" name="sync_really_delete" /> <java-symbol type="string" name="sync_too_many_deletes_desc" /> <java-symbol type="string" name="sync_undo_deletes" /> - <java-symbol type="string" name="terabyteShort" /> <java-symbol type="string" name="text_copied" /> <java-symbol type="string" name="time_of_day" /> <java-symbol type="string" name="time_picker_decrement_hour_button" /> diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java index 5db6a3e421d5..bfb2fd57975f 100644 --- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java +++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java @@ -273,13 +273,14 @@ public class ActivityThreadTest { newerConfig.orientation = orientation == ORIENTATION_LANDSCAPE ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE; newerConfig.seq = seq + 2; - final ActivityClientRecord r = getActivityClientRecord(activity); - activityThread.updatePendingActivityConfiguration(r, newerConfig); + activityThread.updatePendingActivityConfiguration(activity.getActivityToken(), + newerConfig); final Configuration olderConfig = new Configuration(); olderConfig.orientation = orientation; olderConfig.seq = seq + 1; + final ActivityClientRecord r = getActivityClientRecord(activity); activityThread.handleActivityConfigurationChanged(r, olderConfig, INVALID_DISPLAY); assertEquals(numOfConfig, activity.mNumOfConfigChanges); assertEquals(olderConfig.orientation, activity.mConfig.orientation); @@ -504,7 +505,8 @@ public class ActivityThreadTest { ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT; final ActivityClientRecord r = getActivityClientRecord(activity); - activityThread.updatePendingActivityConfiguration(r, newActivityConfig); + activityThread.updatePendingActivityConfiguration(activity.getActivityToken(), + newActivityConfig); activityThread.handleActivityConfigurationChanged(r, newActivityConfig, INVALID_DISPLAY); diff --git a/core/tests/utiltests/src/com/android/internal/util/StateMachineTest.java b/core/tests/utiltests/src/com/android/internal/util/StateMachineTest.java deleted file mode 100644 index b85cb9cec47d..000000000000 --- a/core/tests/utiltests/src/com/android/internal/util/StateMachineTest.java +++ /dev/null @@ -1,2022 +0,0 @@ -/** - * Copyright (C) 2009 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.internal.util; - -import android.os.Debug; -import android.os.HandlerThread; -import android.os.Looper; -import android.os.Message; -import android.os.SystemClock; -import android.os.test.TestLooper; -import android.test.suitebuilder.annotation.MediumTest; -import android.test.suitebuilder.annotation.SmallTest; -import android.util.Log; - -import com.android.internal.util.StateMachine.LogRec; - -import junit.framework.TestCase; - -import java.io.PrintWriter; -import java.io.StringWriter; -import java.util.Collection; -import java.util.Iterator; - -/** - * Test for StateMachine. - */ -public class StateMachineTest extends TestCase { - private static final String ENTER = "enter"; - private static final String EXIT = "exit"; - private static final String ON_QUITTING = "ON_QUITTING"; - - private static final int TEST_CMD_1 = 1; - private static final int TEST_CMD_2 = 2; - private static final int TEST_CMD_3 = 3; - private static final int TEST_CMD_4 = 4; - private static final int TEST_CMD_5 = 5; - private static final int TEST_CMD_6 = 6; - - private static final boolean DBG = true; - private static final boolean WAIT_FOR_DEBUGGER = false; - private static final String TAG = "StateMachineTest"; - - private void sleep(int millis) { - try { - Thread.sleep(millis); - } catch(InterruptedException e) { - } - } - - private void dumpLogRecs(StateMachine sm) { - int size = sm.getLogRecSize(); - tlog("size=" + size + " count=" + sm.getLogRecCount()); - for (int i = 0; i < size; i++) { - LogRec lr = sm.getLogRec(i); - tlog(lr.toString()); - } - } - - private void dumpLogRecs(Collection<LogRec> clr) { - int size = clr.size(); - tlog("size=" + size); - for (LogRec lr : clr) { - tlog(lr.toString()); - } - } - - /** - * Tests {@link StateMachine#toString()}. - */ - class StateMachineToStringTest extends StateMachine { - StateMachineToStringTest(String name) { - super(name); - } - } - - class ExampleState extends State { - String mName; - - ExampleState(String name) { - mName = name; - } - - @Override - public String getName() { - return mName; - } - } - - @SmallTest - public void testToStringSucceedsEvenIfMachineHasNoStates() throws Exception { - StateMachine stateMachine = new StateMachineToStringTest("TestStateMachine"); - assertTrue(stateMachine.toString().contains("TestStateMachine")); - } - - @SmallTest - public void testToStringSucceedsEvenIfStateHasNoName() throws Exception { - StateMachine stateMachine = new StateMachineToStringTest("TestStateMachine"); - State exampleState = new ExampleState(null); - stateMachine.addState(exampleState); - stateMachine.setInitialState(exampleState); - stateMachine.start(); - assertTrue(stateMachine.toString().contains("TestStateMachine")); - assertTrue(stateMachine.toString().contains("(null)")); - } - - @SmallTest - public void testToStringIncludesMachineAndStateNames() throws Exception { - StateMachine stateMachine = new StateMachineToStringTest("TestStateMachine"); - State exampleState = new ExampleState("exampleState"); - stateMachine.addState(exampleState); - stateMachine.setInitialState(exampleState); - stateMachine.start(); - assertTrue(stateMachine.toString().contains("TestStateMachine")); - assertTrue(stateMachine.toString().contains("exampleState")); - } - - @SmallTest - public void testToStringDoesNotContainMultipleLines() throws Exception { - StateMachine stateMachine = new StateMachineToStringTest("TestStateMachine"); - State exampleState = new ExampleState("exampleState"); - stateMachine.addState(exampleState); - stateMachine.setInitialState(exampleState); - stateMachine.start(); - assertFalse(stateMachine.toString().contains("\n")); - } - - /** - * Tests {@link StateMachine#quit()}. - */ - class StateMachineQuitTest extends StateMachine { - Collection<LogRec> mLogRecs; - - StateMachineQuitTest(String name) { - super(name); - mThisSm = this; - setDbg(DBG); - - // Setup state machine with 1 state - addState(mS1); - - // Set the initial state - setInitialState(mS1); - } - - @Override - public void onQuitting() { - tlog("onQuitting"); - addLogRec(ON_QUITTING); - mLogRecs = mThisSm.copyLogRecs(); - synchronized (mThisSm) { - mThisSm.notifyAll(); - } - } - - class S1 extends State { - @Override - public void exit() { - tlog("S1.exit"); - addLogRec(EXIT); - } - @Override - public boolean processMessage(Message message) { - switch(message.what) { - // Sleep and assume the other messages will be queued up. - case TEST_CMD_1: { - tlog("TEST_CMD_1"); - sleep(500); - quit(); - break; - } - default: { - tlog("default what=" + message.what); - break; - } - } - return HANDLED; - } - } - - private StateMachineQuitTest mThisSm; - private S1 mS1 = new S1(); - } - - @SmallTest - public void testStateMachineQuit() throws Exception { - if (WAIT_FOR_DEBUGGER) Debug.waitForDebugger(); - - StateMachineQuitTest smQuitTest = new StateMachineQuitTest("smQuitTest"); - smQuitTest.start(); - if (smQuitTest.isDbg()) tlog("testStateMachineQuit E"); - - synchronized (smQuitTest) { - - // Send 6 message we'll quit on the first but all 6 should be processed before quitting. - for (int i = 1; i <= 6; i++) { - smQuitTest.sendMessage(smQuitTest.obtainMessage(i)); - } - - try { - // wait for the messages to be handled - smQuitTest.wait(); - } catch (InterruptedException e) { - tloge("testStateMachineQuit: exception while waiting " + e.getMessage()); - } - } - - dumpLogRecs(smQuitTest.mLogRecs); - assertEquals(8, smQuitTest.mLogRecs.size()); - - LogRec lr; - Iterator<LogRec> itr = smQuitTest.mLogRecs.iterator(); - for (int i = 1; i <= 6; i++) { - lr = itr.next(); - assertEquals(i, lr.getWhat()); - assertEquals(smQuitTest.mS1, lr.getState()); - assertEquals(smQuitTest.mS1, lr.getOriginalState()); - } - lr = itr.next(); - assertEquals(EXIT, lr.getInfo()); - assertEquals(smQuitTest.mS1, lr.getState()); - - lr = itr.next(); - assertEquals(ON_QUITTING, lr.getInfo()); - - if (smQuitTest.isDbg()) tlog("testStateMachineQuit X"); - } - - /** - * Tests {@link StateMachine#quitNow()} - */ - class StateMachineQuitNowTest extends StateMachine { - public Collection<LogRec> mLogRecs = null; - - StateMachineQuitNowTest(String name) { - super(name); - mThisSm = this; - setDbg(DBG); - - // Setup state machine with 1 state - addState(mS1); - - // Set the initial state - setInitialState(mS1); - } - - @Override - public void onQuitting() { - tlog("onQuitting"); - addLogRec(ON_QUITTING); - // Get a copy of the log records since we're quitting and they will disappear - mLogRecs = mThisSm.copyLogRecs(); - - synchronized (mThisSm) { - mThisSm.notifyAll(); - } - } - - class S1 extends State { - @Override - public void exit() { - tlog("S1.exit"); - addLogRec(EXIT); - } - @Override - public boolean processMessage(Message message) { - switch(message.what) { - // Sleep and assume the other messages will be queued up. - case TEST_CMD_1: { - tlog("TEST_CMD_1"); - sleep(500); - quitNow(); - break; - } - default: { - tlog("default what=" + message.what); - break; - } - } - return HANDLED; - } - } - - private StateMachineQuitNowTest mThisSm; - private S1 mS1 = new S1(); - } - - @SmallTest - public void testStateMachineQuitNow() throws Exception { - if (WAIT_FOR_DEBUGGER) Debug.waitForDebugger(); - - StateMachineQuitNowTest smQuitNowTest = new StateMachineQuitNowTest("smQuitNowTest"); - smQuitNowTest.start(); - if (smQuitNowTest.isDbg()) tlog("testStateMachineQuitNow E"); - - synchronized (smQuitNowTest) { - - // Send 6 message we'll QuitNow on the first even though - // we send 6 only one will be processed. - for (int i = 1; i <= 6; i++) { - smQuitNowTest.sendMessage(smQuitNowTest.obtainMessage(i)); - } - - try { - // wait for the messages to be handled - smQuitNowTest.wait(); - } catch (InterruptedException e) { - tloge("testStateMachineQuitNow: exception while waiting " + e.getMessage()); - } - } - - tlog("testStateMachineQuiteNow: logRecs=" + smQuitNowTest.mLogRecs); - assertEquals(3, smQuitNowTest.mLogRecs.size()); - - Iterator<LogRec> itr = smQuitNowTest.mLogRecs.iterator(); - LogRec lr = itr.next(); - assertEquals(1, lr.getWhat()); - assertEquals(smQuitNowTest.mS1, lr.getState()); - assertEquals(smQuitNowTest.mS1, lr.getOriginalState()); - - lr = itr.next(); - assertEquals(EXIT, lr.getInfo()); - assertEquals(smQuitNowTest.mS1, lr.getState()); - - lr = itr.next(); - assertEquals(ON_QUITTING, lr.getInfo()); - - if (smQuitNowTest.isDbg()) tlog("testStateMachineQuitNow X"); - } - - /** - * Tests {@link StateMachine#quitNow()} immediately after {@link StateMachine#start()}. - */ - class StateMachineQuitNowAfterStartTest extends StateMachine { - Collection<LogRec> mLogRecs; - - StateMachineQuitNowAfterStartTest(String name, Looper looper) { - super(name, looper); - mThisSm = this; - setDbg(DBG); - - // Setup state machine with 1 state - addState(mS1); - - // Set the initial state - setInitialState(mS1); - } - - @Override - public void onQuitting() { - tlog("onQuitting"); - addLogRec(ON_QUITTING); - mLogRecs = mThisSm.copyLogRecs(); - synchronized (mThisSm) { - mThisSm.notifyAll(); - } - } - - class S1 extends State { - @Override - public void enter() { - tlog("S1.enter"); - addLogRec(ENTER); - } - @Override - public void exit() { - tlog("S1.exit"); - addLogRec(EXIT); - } - @Override - public boolean processMessage(Message message) { - switch(message.what) { - // Sleep and assume the other messages will be queued up. - case TEST_CMD_1: { - tlog("TEST_CMD_1"); - sleep(500); - break; - } - default: { - tlog("default what=" + message.what); - break; - } - } - return HANDLED; - } - } - - private StateMachineQuitNowAfterStartTest mThisSm; - private S1 mS1 = new S1(); - } - - /** - * When quitNow() is called immediately after start(), the QUIT_CMD gets processed - * before the INIT_CMD. This test ensures that the StateMachine can gracefully handle - * this sequencing of messages (QUIT before INIT). - */ - @SmallTest - public void testStateMachineQuitNowAfterStart() throws Exception { - if (WAIT_FOR_DEBUGGER) Debug.waitForDebugger(); - - TestLooper testLooper = new TestLooper(); - StateMachineQuitNowAfterStartTest smQuitNowAfterStartTest = - new StateMachineQuitNowAfterStartTest( - "smQuitNowAfterStartTest", testLooper.getLooper()); - smQuitNowAfterStartTest.start(); - smQuitNowAfterStartTest.quitNow(); - if (smQuitNowAfterStartTest.isDbg()) tlog("testStateMachineQuitNowAfterStart E"); - - testLooper.dispatchAll(); - dumpLogRecs(smQuitNowAfterStartTest.mLogRecs); - assertEquals(2, smQuitNowAfterStartTest.mLogRecs.size()); - - LogRec lr; - Iterator<LogRec> itr = smQuitNowAfterStartTest.mLogRecs.iterator(); - lr = itr.next(); - assertEquals(EXIT, lr.getInfo()); - assertEquals(smQuitNowAfterStartTest.mS1, lr.getState()); - - lr = itr.next(); - assertEquals(ON_QUITTING, lr.getInfo()); - - if (smQuitNowAfterStartTest.isDbg()) tlog("testStateMachineQuitNowAfterStart X"); - } - - /** - * Test enter/exit can use transitionTo - */ - class StateMachineEnterExitTransitionToTest extends StateMachine { - - StateMachineEnterExitTransitionToTest(String name) { - super(name); - mThisSm = this; - setDbg(DBG); - - // Setup state machine with 1 state - addState(mS1); - addState(mS2); - addState(mS3); - addState(mS4); - - // Set the initial state - setInitialState(mS1); - } - - class S1 extends State { - @Override - public void enter() { - // Test transitions in enter on the initial state work - addLogRec(ENTER); - transitionTo(mS2); - tlog("S1.enter"); - } - @Override - public void exit() { - addLogRec(EXIT); - tlog("S1.exit"); - } - } - - class S2 extends State { - @Override - public void enter() { - addLogRec(ENTER); - tlog("S2.enter"); - } - @Override - public void exit() { - // Test transition in exit work - transitionTo(mS4); - - assertEquals(TEST_CMD_1, getCurrentMessage().what); - addLogRec(EXIT); - - tlog("S2.exit"); - } - @Override - public boolean processMessage(Message message) { - // Start a transition to S3 but it will be - // changed to a transition to S4 in exit - transitionTo(mS3); - tlog("S2.processMessage"); - return HANDLED; - } - } - - class S3 extends State { - @Override - public void enter() { - addLogRec(ENTER); - tlog("S3.enter"); - } - @Override - public void exit() { - addLogRec(EXIT); - tlog("S3.exit"); - } - } - - class S4 extends State { - @Override - public void enter() { - addLogRec(ENTER); - // Test that we can do halting in an enter/exit - transitionToHaltingState(); - tlog("S4.enter"); - } - @Override - public void exit() { - addLogRec(EXIT); - tlog("S4.exit"); - } - } - - @Override - protected void onHalting() { - synchronized (mThisSm) { - mThisSm.notifyAll(); - } - } - - private StateMachineEnterExitTransitionToTest mThisSm; - private S1 mS1 = new S1(); - private S2 mS2 = new S2(); - private S3 mS3 = new S3(); - private S4 mS4 = new S4(); - } - - @SmallTest - public void testStateMachineEnterExitTransitionToTest() throws Exception { - //if (WAIT_FOR_DEBUGGER) Debug.waitForDebugger(); - - StateMachineEnterExitTransitionToTest smEnterExitTransitionToTest = - new StateMachineEnterExitTransitionToTest("smEnterExitTransitionToTest"); - smEnterExitTransitionToTest.start(); - if (smEnterExitTransitionToTest.isDbg()) { - tlog("testStateMachineEnterExitTransitionToTest E"); - } - - synchronized (smEnterExitTransitionToTest) { - smEnterExitTransitionToTest.sendMessage(TEST_CMD_1); - - try { - // wait for the messages to be handled - smEnterExitTransitionToTest.wait(); - } catch (InterruptedException e) { - tloge("testStateMachineEnterExitTransitionToTest: exception while waiting " - + e.getMessage()); - } - } - - dumpLogRecs(smEnterExitTransitionToTest); - - assertEquals(9, smEnterExitTransitionToTest.getLogRecCount()); - LogRec lr; - - lr = smEnterExitTransitionToTest.getLogRec(0); - assertEquals(ENTER, lr.getInfo()); - assertEquals(smEnterExitTransitionToTest.mS1, lr.getState()); - - lr = smEnterExitTransitionToTest.getLogRec(1); - assertEquals(EXIT, lr.getInfo()); - assertEquals(smEnterExitTransitionToTest.mS1, lr.getState()); - - lr = smEnterExitTransitionToTest.getLogRec(2); - assertEquals(ENTER, lr.getInfo()); - assertEquals(smEnterExitTransitionToTest.mS2, lr.getState()); - - lr = smEnterExitTransitionToTest.getLogRec(3); - assertEquals(TEST_CMD_1, lr.getWhat()); - assertEquals(smEnterExitTransitionToTest.mS2, lr.getState()); - assertEquals(smEnterExitTransitionToTest.mS2, lr.getOriginalState()); - assertEquals(smEnterExitTransitionToTest.mS3, lr.getDestState()); - - lr = smEnterExitTransitionToTest.getLogRec(4); - assertEquals(TEST_CMD_1, lr.getWhat()); - assertEquals(smEnterExitTransitionToTest.mS2, lr.getState()); - assertEquals(smEnterExitTransitionToTest.mS2, lr.getOriginalState()); - assertEquals(smEnterExitTransitionToTest.mS4, lr.getDestState()); - assertEquals(EXIT, lr.getInfo()); - - lr = smEnterExitTransitionToTest.getLogRec(5); - assertEquals(TEST_CMD_1, lr.getWhat()); - assertEquals(ENTER, lr.getInfo()); - assertEquals(smEnterExitTransitionToTest.mS3, lr.getState()); - assertEquals(smEnterExitTransitionToTest.mS3, lr.getOriginalState()); - assertEquals(smEnterExitTransitionToTest.mS4, lr.getDestState()); - - lr = smEnterExitTransitionToTest.getLogRec(6); - assertEquals(TEST_CMD_1, lr.getWhat()); - assertEquals(EXIT, lr.getInfo()); - assertEquals(smEnterExitTransitionToTest.mS3, lr.getState()); - assertEquals(smEnterExitTransitionToTest.mS3, lr.getOriginalState()); - assertEquals(smEnterExitTransitionToTest.mS4, lr.getDestState()); - - lr = smEnterExitTransitionToTest.getLogRec(7); - assertEquals(TEST_CMD_1, lr.getWhat()); - assertEquals(ENTER, lr.getInfo()); - assertEquals(smEnterExitTransitionToTest.mS4, lr.getState()); - assertEquals(smEnterExitTransitionToTest.mS4, lr.getOriginalState()); - assertEquals(smEnterExitTransitionToTest.mS4, lr.getDestState()); - - lr = smEnterExitTransitionToTest.getLogRec(8); - assertEquals(TEST_CMD_1, lr.getWhat()); - assertEquals(EXIT, lr.getInfo()); - assertEquals(smEnterExitTransitionToTest.mS4, lr.getState()); - assertEquals(smEnterExitTransitionToTest.mS4, lr.getOriginalState()); - - if (smEnterExitTransitionToTest.isDbg()) { - tlog("testStateMachineEnterExitTransitionToTest X"); - } - } - - /** - * Tests that ProcessedMessage works as a circular buffer. - */ - class StateMachine0 extends StateMachine { - StateMachine0(String name) { - super(name); - mThisSm = this; - setDbg(DBG); - setLogRecSize(3); - - // Setup state machine with 1 state - addState(mS1); - - // Set the initial state - setInitialState(mS1); - } - - class S1 extends State { - @Override - public boolean processMessage(Message message) { - if (message.what == TEST_CMD_6) { - transitionToHaltingState(); - } - return HANDLED; - } - } - - @Override - protected void onHalting() { - synchronized (mThisSm) { - mThisSm.notifyAll(); - } - } - - private StateMachine0 mThisSm; - private S1 mS1 = new S1(); - } - - @SmallTest - public void testStateMachine0() throws Exception { - //if (WAIT_FOR_DEBUGGER) Debug.waitForDebugger(); - - StateMachine0 sm0 = new StateMachine0("sm0"); - sm0.start(); - if (sm0.isDbg()) tlog("testStateMachine0 E"); - - synchronized (sm0) { - // Send 6 messages - for (int i = 1; i <= 6; i++) { - sm0.sendMessage(sm0.obtainMessage(i)); - } - - try { - // wait for the messages to be handled - sm0.wait(); - } catch (InterruptedException e) { - tloge("testStateMachine0: exception while waiting " + e.getMessage()); - } - } - - assertEquals(6, sm0.getLogRecCount()); - assertEquals(3, sm0.getLogRecSize()); - - dumpLogRecs(sm0); - - LogRec lr; - lr = sm0.getLogRec(0); - assertEquals(TEST_CMD_4, lr.getWhat()); - assertEquals(sm0.mS1, lr.getState()); - assertEquals(sm0.mS1, lr.getOriginalState()); - - lr = sm0.getLogRec(1); - assertEquals(TEST_CMD_5, lr.getWhat()); - assertEquals(sm0.mS1, lr.getState()); - assertEquals(sm0.mS1, lr.getOriginalState()); - - lr = sm0.getLogRec(2); - assertEquals(TEST_CMD_6, lr.getWhat()); - assertEquals(sm0.mS1, lr.getState()); - assertEquals(sm0.mS1, lr.getOriginalState()); - - if (sm0.isDbg()) tlog("testStateMachine0 X"); - } - - /** - * This tests enter/exit and transitions to the same state. - * The state machine has one state, it receives two messages - * in state mS1. With the first message it transitions to - * itself which causes it to be exited and reentered. - */ - class StateMachine1 extends StateMachine { - StateMachine1(String name) { - super(name); - mThisSm = this; - setDbg(DBG); - - // Setup state machine with 1 state - addState(mS1); - - // Set the initial state - setInitialState(mS1); - if (DBG) tlog("StateMachine1: ctor X"); - } - - class S1 extends State { - @Override - public void enter() { - mEnterCount++; - } - @Override - public void exit() { - mExitCount++; - } - @Override - public boolean processMessage(Message message) { - if (message.what == TEST_CMD_1) { - assertEquals(1, mEnterCount); - assertEquals(0, mExitCount); - transitionTo(mS1); - } else if (message.what == TEST_CMD_2) { - assertEquals(2, mEnterCount); - assertEquals(1, mExitCount); - transitionToHaltingState(); - } - return HANDLED; - } - } - - @Override - protected void onHalting() { - synchronized (mThisSm) { - mThisSm.notifyAll(); - } - } - - private StateMachine1 mThisSm; - private S1 mS1 = new S1(); - - private int mEnterCount; - private int mExitCount; - } - - @MediumTest - public void testStateMachine1() throws Exception { - StateMachine1 sm1 = new StateMachine1("sm1"); - sm1.start(); - if (sm1.isDbg()) tlog("testStateMachine1 E"); - - synchronized (sm1) { - // Send two messages - sm1.sendMessage(TEST_CMD_1); - sm1.sendMessage(TEST_CMD_2); - - try { - // wait for the messages to be handled - sm1.wait(); - } catch (InterruptedException e) { - tloge("testStateMachine1: exception while waiting " + e.getMessage()); - } - } - - assertEquals(2, sm1.mEnterCount); - assertEquals(2, sm1.mExitCount); - - assertEquals(2, sm1.getLogRecSize()); - - LogRec lr; - lr = sm1.getLogRec(0); - assertEquals(TEST_CMD_1, lr.getWhat()); - assertEquals(sm1.mS1, lr.getState()); - assertEquals(sm1.mS1, lr.getOriginalState()); - - lr = sm1.getLogRec(1); - assertEquals(TEST_CMD_2, lr.getWhat()); - assertEquals(sm1.mS1, lr.getState()); - assertEquals(sm1.mS1, lr.getOriginalState()); - - assertEquals(2, sm1.mEnterCount); - assertEquals(2, sm1.mExitCount); - - if (sm1.isDbg()) tlog("testStateMachine1 X"); - } - - /** - * Test deferring messages and states with no parents. The state machine - * has two states, it receives two messages in state mS1 deferring them - * until what == TEST_CMD_2 and then transitions to state mS2. State - * mS2 then receives both of the deferred messages first TEST_CMD_1 and - * then TEST_CMD_2. - */ - class StateMachine2 extends StateMachine { - StateMachine2(String name) { - super(name); - mThisSm = this; - setDbg(DBG); - - // Setup the hierarchy - addState(mS1); - addState(mS2); - - // Set the initial state - setInitialState(mS1); - if (DBG) tlog("StateMachine2: ctor X"); - } - - class S1 extends State { - @Override - public void enter() { - mDidEnter = true; - } - @Override - public void exit() { - mDidExit = true; - } - @Override - public boolean processMessage(Message message) { - deferMessage(message); - if (message.what == TEST_CMD_2) { - transitionTo(mS2); - } - return HANDLED; - } - } - - class S2 extends State { - @Override - public boolean processMessage(Message message) { - if (message.what == TEST_CMD_2) { - transitionToHaltingState(); - } - return HANDLED; - } - } - - @Override - protected void onHalting() { - synchronized (mThisSm) { - mThisSm.notifyAll(); - } - } - - private StateMachine2 mThisSm; - private S1 mS1 = new S1(); - private S2 mS2 = new S2(); - - private boolean mDidEnter = false; - private boolean mDidExit = false; - } - - @MediumTest - public void testStateMachine2() throws Exception { - StateMachine2 sm2 = new StateMachine2("sm2"); - sm2.start(); - if (sm2.isDbg()) tlog("testStateMachine2 E"); - - synchronized (sm2) { - // Send two messages - sm2.sendMessage(TEST_CMD_1); - sm2.sendMessage(TEST_CMD_2); - - try { - // wait for the messages to be handled - sm2.wait(); - } catch (InterruptedException e) { - tloge("testStateMachine2: exception while waiting " + e.getMessage()); - } - } - - assertEquals(4, sm2.getLogRecSize()); - - LogRec lr; - lr = sm2.getLogRec(0); - assertEquals(TEST_CMD_1, lr.getWhat()); - assertEquals(sm2.mS1, lr.getState()); - - lr = sm2.getLogRec(1); - assertEquals(TEST_CMD_2, lr.getWhat()); - assertEquals(sm2.mS1, lr.getState()); - - lr = sm2.getLogRec(2); - assertEquals(TEST_CMD_1, lr.getWhat()); - assertEquals(sm2.mS2, lr.getState()); - - lr = sm2.getLogRec(3); - assertEquals(TEST_CMD_2, lr.getWhat()); - assertEquals(sm2.mS2, lr.getState()); - - assertTrue(sm2.mDidEnter); - assertTrue(sm2.mDidExit); - - if (sm2.isDbg()) tlog("testStateMachine2 X"); - } - - /** - * Test that unhandled messages in a child are handled by the parent. - * When TEST_CMD_2 is received. - */ - class StateMachine3 extends StateMachine { - StateMachine3(String name) { - super(name); - mThisSm = this; - setDbg(DBG); - - // Setup the simplest hierarchy of two states - // mParentState and mChildState. - // (Use indentation to help visualize hierarchy) - addState(mParentState); - addState(mChildState, mParentState); - - // Set the initial state will be the child - setInitialState(mChildState); - if (DBG) tlog("StateMachine3: ctor X"); - } - - class ParentState extends State { - @Override - public boolean processMessage(Message message) { - if (message.what == TEST_CMD_2) { - transitionToHaltingState(); - } - return HANDLED; - } - } - - class ChildState extends State { - @Override - public boolean processMessage(Message message) { - return NOT_HANDLED; - } - } - - @Override - protected void onHalting() { - synchronized (mThisSm) { - mThisSm.notifyAll(); - } - } - - private StateMachine3 mThisSm; - private ParentState mParentState = new ParentState(); - private ChildState mChildState = new ChildState(); - } - - @MediumTest - public void testStateMachine3() throws Exception { - StateMachine3 sm3 = new StateMachine3("sm3"); - sm3.start(); - if (sm3.isDbg()) tlog("testStateMachine3 E"); - - synchronized (sm3) { - // Send two messages - sm3.sendMessage(TEST_CMD_1); - sm3.sendMessage(TEST_CMD_2); - - try { - // wait for the messages to be handled - sm3.wait(); - } catch (InterruptedException e) { - tloge("testStateMachine3: exception while waiting " + e.getMessage()); - } - } - - assertEquals(2, sm3.getLogRecSize()); - - LogRec lr; - lr = sm3.getLogRec(0); - assertEquals(TEST_CMD_1, lr.getWhat()); - assertEquals(sm3.mParentState, lr.getState()); - assertEquals(sm3.mChildState, lr.getOriginalState()); - - lr = sm3.getLogRec(1); - assertEquals(TEST_CMD_2, lr.getWhat()); - assertEquals(sm3.mParentState, lr.getState()); - assertEquals(sm3.mChildState, lr.getOriginalState()); - - if (sm3.isDbg()) tlog("testStateMachine3 X"); - } - - /** - * Test a hierarchy of 3 states a parent and two children - * with transition from child 1 to child 2 and child 2 - * lets the parent handle the messages. - */ - class StateMachine4 extends StateMachine { - StateMachine4(String name) { - super(name); - mThisSm = this; - setDbg(DBG); - - // Setup a hierarchy of three states - // mParentState, mChildState1 & mChildState2 - // (Use indentation to help visualize hierarchy) - addState(mParentState); - addState(mChildState1, mParentState); - addState(mChildState2, mParentState); - - // Set the initial state will be child 1 - setInitialState(mChildState1); - if (DBG) tlog("StateMachine4: ctor X"); - } - - class ParentState extends State { - @Override - public boolean processMessage(Message message) { - if (message.what == TEST_CMD_2) { - transitionToHaltingState(); - } - return HANDLED; - } - } - - class ChildState1 extends State { - @Override - public boolean processMessage(Message message) { - transitionTo(mChildState2); - return HANDLED; - } - } - - class ChildState2 extends State { - @Override - public boolean processMessage(Message message) { - return NOT_HANDLED; - } - } - - @Override - protected void onHalting() { - synchronized (mThisSm) { - mThisSm.notifyAll(); - } - } - - private StateMachine4 mThisSm; - private ParentState mParentState = new ParentState(); - private ChildState1 mChildState1 = new ChildState1(); - private ChildState2 mChildState2 = new ChildState2(); - } - - @MediumTest - public void testStateMachine4() throws Exception { - StateMachine4 sm4 = new StateMachine4("sm4"); - sm4.start(); - if (sm4.isDbg()) tlog("testStateMachine4 E"); - - synchronized (sm4) { - // Send two messages - sm4.sendMessage(TEST_CMD_1); - sm4.sendMessage(TEST_CMD_2); - - try { - // wait for the messages to be handled - sm4.wait(); - } catch (InterruptedException e) { - tloge("testStateMachine4: exception while waiting " + e.getMessage()); - } - } - - - assertEquals(2, sm4.getLogRecSize()); - - LogRec lr; - lr = sm4.getLogRec(0); - assertEquals(TEST_CMD_1, lr.getWhat()); - assertEquals(sm4.mChildState1, lr.getState()); - assertEquals(sm4.mChildState1, lr.getOriginalState()); - - lr = sm4.getLogRec(1); - assertEquals(TEST_CMD_2, lr.getWhat()); - assertEquals(sm4.mParentState, lr.getState()); - assertEquals(sm4.mChildState2, lr.getOriginalState()); - - if (sm4.isDbg()) tlog("testStateMachine4 X"); - } - - /** - * Test transition from one child to another of a "complex" - * hierarchy with two parents and multiple children. - */ - class StateMachine5 extends StateMachine { - StateMachine5(String name) { - super(name); - mThisSm = this; - setDbg(DBG); - - // Setup a hierarchy with two parents and some children. - // (Use indentation to help visualize hierarchy) - addState(mParentState1); - addState(mChildState1, mParentState1); - addState(mChildState2, mParentState1); - - addState(mParentState2); - addState(mChildState3, mParentState2); - addState(mChildState4, mParentState2); - addState(mChildState5, mChildState4); - - // Set the initial state will be the child - setInitialState(mChildState1); - if (DBG) tlog("StateMachine5: ctor X"); - } - - class ParentState1 extends State { - @Override - public void enter() { - mParentState1EnterCount += 1; - } - @Override - public void exit() { - mParentState1ExitCount += 1; - } - @Override - public boolean processMessage(Message message) { - return HANDLED; - } - } - - class ChildState1 extends State { - @Override - public void enter() { - mChildState1EnterCount += 1; - } - @Override - public void exit() { - mChildState1ExitCount += 1; - } - @Override - public boolean processMessage(Message message) { - assertEquals(1, mParentState1EnterCount); - assertEquals(0, mParentState1ExitCount); - assertEquals(1, mChildState1EnterCount); - assertEquals(0, mChildState1ExitCount); - assertEquals(0, mChildState2EnterCount); - assertEquals(0, mChildState2ExitCount); - assertEquals(0, mParentState2EnterCount); - assertEquals(0, mParentState2ExitCount); - assertEquals(0, mChildState3EnterCount); - assertEquals(0, mChildState3ExitCount); - assertEquals(0, mChildState4EnterCount); - assertEquals(0, mChildState4ExitCount); - assertEquals(0, mChildState5EnterCount); - assertEquals(0, mChildState5ExitCount); - - transitionTo(mChildState2); - return HANDLED; - } - } - - class ChildState2 extends State { - @Override - public void enter() { - mChildState2EnterCount += 1; - } - @Override - public void exit() { - mChildState2ExitCount += 1; - } - @Override - public boolean processMessage(Message message) { - assertEquals(1, mParentState1EnterCount); - assertEquals(0, mParentState1ExitCount); - assertEquals(1, mChildState1EnterCount); - assertEquals(1, mChildState1ExitCount); - assertEquals(1, mChildState2EnterCount); - assertEquals(0, mChildState2ExitCount); - assertEquals(0, mParentState2EnterCount); - assertEquals(0, mParentState2ExitCount); - assertEquals(0, mChildState3EnterCount); - assertEquals(0, mChildState3ExitCount); - assertEquals(0, mChildState4EnterCount); - assertEquals(0, mChildState4ExitCount); - assertEquals(0, mChildState5EnterCount); - assertEquals(0, mChildState5ExitCount); - - transitionTo(mChildState5); - return HANDLED; - } - } - - class ParentState2 extends State { - @Override - public void enter() { - mParentState2EnterCount += 1; - } - @Override - public void exit() { - mParentState2ExitCount += 1; - } - @Override - public boolean processMessage(Message message) { - assertEquals(1, mParentState1EnterCount); - assertEquals(1, mParentState1ExitCount); - assertEquals(1, mChildState1EnterCount); - assertEquals(1, mChildState1ExitCount); - assertEquals(1, mChildState2EnterCount); - assertEquals(1, mChildState2ExitCount); - assertEquals(2, mParentState2EnterCount); - assertEquals(1, mParentState2ExitCount); - assertEquals(1, mChildState3EnterCount); - assertEquals(1, mChildState3ExitCount); - assertEquals(2, mChildState4EnterCount); - assertEquals(2, mChildState4ExitCount); - assertEquals(1, mChildState5EnterCount); - assertEquals(1, mChildState5ExitCount); - - transitionToHaltingState(); - return HANDLED; - } - } - - class ChildState3 extends State { - @Override - public void enter() { - mChildState3EnterCount += 1; - } - @Override - public void exit() { - mChildState3ExitCount += 1; - } - @Override - public boolean processMessage(Message message) { - assertEquals(1, mParentState1EnterCount); - assertEquals(1, mParentState1ExitCount); - assertEquals(1, mChildState1EnterCount); - assertEquals(1, mChildState1ExitCount); - assertEquals(1, mChildState2EnterCount); - assertEquals(1, mChildState2ExitCount); - assertEquals(1, mParentState2EnterCount); - assertEquals(0, mParentState2ExitCount); - assertEquals(1, mChildState3EnterCount); - assertEquals(0, mChildState3ExitCount); - assertEquals(1, mChildState4EnterCount); - assertEquals(1, mChildState4ExitCount); - assertEquals(1, mChildState5EnterCount); - assertEquals(1, mChildState5ExitCount); - - transitionTo(mChildState4); - return HANDLED; - } - } - - class ChildState4 extends State { - @Override - public void enter() { - mChildState4EnterCount += 1; - } - @Override - public void exit() { - mChildState4ExitCount += 1; - } - @Override - public boolean processMessage(Message message) { - assertEquals(1, mParentState1EnterCount); - assertEquals(1, mParentState1ExitCount); - assertEquals(1, mChildState1EnterCount); - assertEquals(1, mChildState1ExitCount); - assertEquals(1, mChildState2EnterCount); - assertEquals(1, mChildState2ExitCount); - assertEquals(1, mParentState2EnterCount); - assertEquals(0, mParentState2ExitCount); - assertEquals(1, mChildState3EnterCount); - assertEquals(1, mChildState3ExitCount); - assertEquals(2, mChildState4EnterCount); - assertEquals(1, mChildState4ExitCount); - assertEquals(1, mChildState5EnterCount); - assertEquals(1, mChildState5ExitCount); - - transitionTo(mParentState2); - return HANDLED; - } - } - - class ChildState5 extends State { - @Override - public void enter() { - mChildState5EnterCount += 1; - } - @Override - public void exit() { - mChildState5ExitCount += 1; - } - @Override - public boolean processMessage(Message message) { - assertEquals(1, mParentState1EnterCount); - assertEquals(1, mParentState1ExitCount); - assertEquals(1, mChildState1EnterCount); - assertEquals(1, mChildState1ExitCount); - assertEquals(1, mChildState2EnterCount); - assertEquals(1, mChildState2ExitCount); - assertEquals(1, mParentState2EnterCount); - assertEquals(0, mParentState2ExitCount); - assertEquals(0, mChildState3EnterCount); - assertEquals(0, mChildState3ExitCount); - assertEquals(1, mChildState4EnterCount); - assertEquals(0, mChildState4ExitCount); - assertEquals(1, mChildState5EnterCount); - assertEquals(0, mChildState5ExitCount); - - transitionTo(mChildState3); - return HANDLED; - } - } - - @Override - protected void onHalting() { - synchronized (mThisSm) { - mThisSm.notifyAll(); - } - } - - private StateMachine5 mThisSm; - private ParentState1 mParentState1 = new ParentState1(); - private ChildState1 mChildState1 = new ChildState1(); - private ChildState2 mChildState2 = new ChildState2(); - private ParentState2 mParentState2 = new ParentState2(); - private ChildState3 mChildState3 = new ChildState3(); - private ChildState4 mChildState4 = new ChildState4(); - private ChildState5 mChildState5 = new ChildState5(); - - private int mParentState1EnterCount = 0; - private int mParentState1ExitCount = 0; - private int mChildState1EnterCount = 0; - private int mChildState1ExitCount = 0; - private int mChildState2EnterCount = 0; - private int mChildState2ExitCount = 0; - private int mParentState2EnterCount = 0; - private int mParentState2ExitCount = 0; - private int mChildState3EnterCount = 0; - private int mChildState3ExitCount = 0; - private int mChildState4EnterCount = 0; - private int mChildState4ExitCount = 0; - private int mChildState5EnterCount = 0; - private int mChildState5ExitCount = 0; - } - - @MediumTest - public void testStateMachine5() throws Exception { - StateMachine5 sm5 = new StateMachine5("sm5"); - sm5.start(); - if (sm5.isDbg()) tlog("testStateMachine5 E"); - - synchronized (sm5) { - // Send 6 messages - sm5.sendMessage(TEST_CMD_1); - sm5.sendMessage(TEST_CMD_2); - sm5.sendMessage(TEST_CMD_3); - sm5.sendMessage(TEST_CMD_4); - sm5.sendMessage(TEST_CMD_5); - sm5.sendMessage(TEST_CMD_6); - - try { - // wait for the messages to be handled - sm5.wait(); - } catch (InterruptedException e) { - tloge("testStateMachine5: exception while waiting " + e.getMessage()); - } - } - - - assertEquals(6, sm5.getLogRecSize()); - - assertEquals(1, sm5.mParentState1EnterCount); - assertEquals(1, sm5.mParentState1ExitCount); - assertEquals(1, sm5.mChildState1EnterCount); - assertEquals(1, sm5.mChildState1ExitCount); - assertEquals(1, sm5.mChildState2EnterCount); - assertEquals(1, sm5.mChildState2ExitCount); - assertEquals(2, sm5.mParentState2EnterCount); - assertEquals(2, sm5.mParentState2ExitCount); - assertEquals(1, sm5.mChildState3EnterCount); - assertEquals(1, sm5.mChildState3ExitCount); - assertEquals(2, sm5.mChildState4EnterCount); - assertEquals(2, sm5.mChildState4ExitCount); - assertEquals(1, sm5.mChildState5EnterCount); - assertEquals(1, sm5.mChildState5ExitCount); - - LogRec lr; - lr = sm5.getLogRec(0); - assertEquals(TEST_CMD_1, lr.getWhat()); - assertEquals(sm5.mChildState1, lr.getState()); - assertEquals(sm5.mChildState1, lr.getOriginalState()); - - lr = sm5.getLogRec(1); - assertEquals(TEST_CMD_2, lr.getWhat()); - assertEquals(sm5.mChildState2, lr.getState()); - assertEquals(sm5.mChildState2, lr.getOriginalState()); - - lr = sm5.getLogRec(2); - assertEquals(TEST_CMD_3, lr.getWhat()); - assertEquals(sm5.mChildState5, lr.getState()); - assertEquals(sm5.mChildState5, lr.getOriginalState()); - - lr = sm5.getLogRec(3); - assertEquals(TEST_CMD_4, lr.getWhat()); - assertEquals(sm5.mChildState3, lr.getState()); - assertEquals(sm5.mChildState3, lr.getOriginalState()); - - lr = sm5.getLogRec(4); - assertEquals(TEST_CMD_5, lr.getWhat()); - assertEquals(sm5.mChildState4, lr.getState()); - assertEquals(sm5.mChildState4, lr.getOriginalState()); - - lr = sm5.getLogRec(5); - assertEquals(TEST_CMD_6, lr.getWhat()); - assertEquals(sm5.mParentState2, lr.getState()); - assertEquals(sm5.mParentState2, lr.getOriginalState()); - - if (sm5.isDbg()) tlog("testStateMachine5 X"); - } - - /** - * Test that the initial state enter is invoked immediately - * after construction and before any other messages arrive and that - * sendMessageDelayed works. - */ - class StateMachine6 extends StateMachine { - StateMachine6(String name) { - super(name); - mThisSm = this; - setDbg(DBG); - - // Setup state machine with 1 state - addState(mS1); - - // Set the initial state - setInitialState(mS1); - if (DBG) tlog("StateMachine6: ctor X"); - } - - class S1 extends State { - @Override - public void enter() { - sendMessage(TEST_CMD_1); - } - @Override - public boolean processMessage(Message message) { - if (message.what == TEST_CMD_1) { - mArrivalTimeMsg1 = SystemClock.elapsedRealtime(); - } else if (message.what == TEST_CMD_2) { - mArrivalTimeMsg2 = SystemClock.elapsedRealtime(); - transitionToHaltingState(); - } - return HANDLED; - } - } - - @Override - protected void onHalting() { - synchronized (mThisSm) { - mThisSm.notifyAll(); - } - } - - private StateMachine6 mThisSm; - private S1 mS1 = new S1(); - - private long mArrivalTimeMsg1; - private long mArrivalTimeMsg2; - } - - @MediumTest - public void testStateMachine6() throws Exception { - final int DELAY_TIME = 250; - final int DELAY_FUDGE = 20; - - StateMachine6 sm6 = new StateMachine6("sm6"); - sm6.start(); - if (sm6.isDbg()) tlog("testStateMachine6 E"); - - synchronized (sm6) { - // Send a message - sm6.sendMessageDelayed(TEST_CMD_2, DELAY_TIME); - - try { - // wait for the messages to be handled - sm6.wait(); - } catch (InterruptedException e) { - tloge("testStateMachine6: exception while waiting " + e.getMessage()); - } - } - - /** - * TEST_CMD_1 was sent in enter and must always have been processed - * immediately after construction and hence the arrival time difference - * should always >= to the DELAY_TIME - */ - long arrivalTimeDiff = sm6.mArrivalTimeMsg2 - sm6.mArrivalTimeMsg1; - long expectedDelay = DELAY_TIME - DELAY_FUDGE; - if (sm6.isDbg()) tlog("testStateMachine6: expect " + arrivalTimeDiff - + " >= " + expectedDelay); - assertTrue(arrivalTimeDiff >= expectedDelay); - - if (sm6.isDbg()) tlog("testStateMachine6 X"); - } - - /** - * Test that enter is invoked immediately after exit. This validates - * that enter can be used to send a watch dog message for its state. - */ - class StateMachine7 extends StateMachine { - private final int SM7_DELAY_TIME = 250; - - StateMachine7(String name) { - super(name); - mThisSm = this; - setDbg(DBG); - - // Setup state machine with 1 state - addState(mS1); - addState(mS2); - - // Set the initial state - setInitialState(mS1); - if (DBG) tlog("StateMachine7: ctor X"); - } - - class S1 extends State { - @Override - public void exit() { - sendMessage(TEST_CMD_2); - } - @Override - public boolean processMessage(Message message) { - transitionTo(mS2); - return HANDLED; - } - } - - class S2 extends State { - @Override - public void enter() { - // Send a delayed message as a watch dog - sendMessageDelayed(TEST_CMD_3, SM7_DELAY_TIME); - } - @Override - public boolean processMessage(Message message) { - if (message.what == TEST_CMD_2) { - mMsgCount += 1; - mArrivalTimeMsg2 = SystemClock.elapsedRealtime(); - } else if (message.what == TEST_CMD_3) { - mMsgCount += 1; - mArrivalTimeMsg3 = SystemClock.elapsedRealtime(); - } - - if (mMsgCount == 2) { - transitionToHaltingState(); - } - return HANDLED; - } - } - - @Override - protected void onHalting() { - synchronized (mThisSm) { - mThisSm.notifyAll(); - } - } - - private StateMachine7 mThisSm; - private S1 mS1 = new S1(); - private S2 mS2 = new S2(); - - private int mMsgCount = 0; - private long mArrivalTimeMsg2; - private long mArrivalTimeMsg3; - } - - @MediumTest - public void testStateMachine7() throws Exception { - final int SM7_DELAY_FUDGE = 20; - - StateMachine7 sm7 = new StateMachine7("sm7"); - sm7.start(); - if (sm7.isDbg()) tlog("testStateMachine7 E"); - - synchronized (sm7) { - // Send a message - sm7.sendMessage(TEST_CMD_1); - - try { - // wait for the messages to be handled - sm7.wait(); - } catch (InterruptedException e) { - tloge("testStateMachine7: exception while waiting " + e.getMessage()); - } - } - - /** - * TEST_CMD_3 was sent in S2.enter with a delay and must always have been - * processed immediately after S1.exit. Since S1.exit sent TEST_CMD_2 - * without a delay the arrival time difference should always >= to SM7_DELAY_TIME. - */ - long arrivalTimeDiff = sm7.mArrivalTimeMsg3 - sm7.mArrivalTimeMsg2; - long expectedDelay = sm7.SM7_DELAY_TIME - SM7_DELAY_FUDGE; - if (sm7.isDbg()) tlog("testStateMachine7: expect " + arrivalTimeDiff - + " >= " + expectedDelay); - assertTrue(arrivalTimeDiff >= expectedDelay); - - if (sm7.isDbg()) tlog("testStateMachine7 X"); - } - - /** - * Test unhandledMessage. - */ - class StateMachineUnhandledMessage extends StateMachine { - StateMachineUnhandledMessage(String name) { - super(name); - mThisSm = this; - setDbg(DBG); - - // Setup state machine with 1 state - addState(mS1); - - // Set the initial state - setInitialState(mS1); - } - @Override - public void unhandledMessage(Message message) { - mUnhandledMessageCount += 1; - } - - class S1 extends State { - @Override - public boolean processMessage(Message message) { - if (message.what == TEST_CMD_2) { - transitionToHaltingState(); - } - return NOT_HANDLED; - } - } - - @Override - protected void onHalting() { - synchronized (mThisSm) { - mThisSm.notifyAll(); - } - } - - private StateMachineUnhandledMessage mThisSm; - private int mUnhandledMessageCount; - private S1 mS1 = new S1(); - } - - @SmallTest - public void testStateMachineUnhandledMessage() throws Exception { - - StateMachineUnhandledMessage sm = new StateMachineUnhandledMessage("smUnhandledMessage"); - sm.start(); - if (sm.isDbg()) tlog("testStateMachineUnhandledMessage E"); - - synchronized (sm) { - // Send 2 messages - for (int i = 1; i <= 2; i++) { - sm.sendMessage(i); - } - - try { - // wait for the messages to be handled - sm.wait(); - } catch (InterruptedException e) { - tloge("testStateMachineUnhandledMessage: exception while waiting " - + e.getMessage()); - } - } - - assertEquals(2, sm.getLogRecSize()); - assertEquals(2, sm.mUnhandledMessageCount); - - if (sm.isDbg()) tlog("testStateMachineUnhandledMessage X"); - } - - /** - * Test state machines sharing the same thread/looper. Multiple instances - * of the same state machine will be created. They will all share the - * same thread and thus each can update <code>sharedCounter</code> which - * will be used to notify testStateMachineSharedThread that the test is - * complete. - */ - class StateMachineSharedThread extends StateMachine { - StateMachineSharedThread(String name, Looper looper, int maxCount) { - super(name, looper); - mMaxCount = maxCount; - setDbg(DBG); - - // Setup state machine with 1 state - addState(mS1); - - // Set the initial state - setInitialState(mS1); - } - - class S1 extends State { - @Override - public boolean processMessage(Message message) { - if (message.what == TEST_CMD_4) { - transitionToHaltingState(); - } - return HANDLED; - } - } - - @Override - protected void onHalting() { - // Update the shared counter, which is OK since all state - // machines are using the same thread. - sharedCounter += 1; - if (sharedCounter == mMaxCount) { - synchronized (waitObject) { - waitObject.notifyAll(); - } - } - } - - private int mMaxCount; - private S1 mS1 = new S1(); - } - private static int sharedCounter = 0; - private static Object waitObject = new Object(); - - @MediumTest - public void testStateMachineSharedThread() throws Exception { - if (DBG) tlog("testStateMachineSharedThread E"); - - // Create and start the handler thread - HandlerThread smThread = new HandlerThread("testStateMachineSharedThread"); - smThread.start(); - - // Create the state machines - StateMachineSharedThread sms[] = new StateMachineSharedThread[10]; - for (int i = 0; i < sms.length; i++) { - sms[i] = new StateMachineSharedThread("smSharedThread", - smThread.getLooper(), sms.length); - sms[i].start(); - } - - synchronized (waitObject) { - // Send messages to each of the state machines - for (StateMachineSharedThread sm : sms) { - for (int i = 1; i <= 4; i++) { - sm.sendMessage(i); - } - } - - // Wait for the last state machine to notify its done - try { - waitObject.wait(); - } catch (InterruptedException e) { - tloge("testStateMachineSharedThread: exception while waiting " - + e.getMessage()); - } - } - - for (StateMachineSharedThread sm : sms) { - assertEquals(4, sm.getLogRecCount()); - for (int i = 0; i < sm.getLogRecSize(); i++) { - LogRec lr = sm.getLogRec(i); - assertEquals(i+1, lr.getWhat()); - assertEquals(sm.mS1, lr.getState()); - assertEquals(sm.mS1, lr.getOriginalState()); - } - } - - if (DBG) tlog("testStateMachineSharedThread X"); - } - - static class Hsm1 extends StateMachine { - private static final String HSM1_TAG = "hsm1"; - - public static final int CMD_1 = 1; - public static final int CMD_2 = 2; - public static final int CMD_3 = 3; - public static final int CMD_4 = 4; - public static final int CMD_5 = 5; - - public static Hsm1 makeHsm1() { - Log.d(HSM1_TAG, "makeHsm1 E"); - Hsm1 sm = new Hsm1(HSM1_TAG); - sm.start(); - Log.d(HSM1_TAG, "makeHsm1 X"); - return sm; - } - - Hsm1(String name) { - super(name); - tlog("ctor E"); - - // Add states, use indentation to show hierarchy - addState(mP1); - addState(mS1, mP1); - addState(mS2, mP1); - addState(mP2); - - // Set the initial state - setInitialState(mS1); - tlog("ctor X"); - } - - class P1 extends State { - @Override - public void enter() { - tlog("P1.enter"); - } - @Override - public void exit() { - tlog("P1.exit"); - } - @Override - public boolean processMessage(Message message) { - boolean retVal; - tlog("P1.processMessage what=" + message.what); - switch(message.what) { - case CMD_2: - // CMD_2 will arrive in mS2 before CMD_3 - sendMessage(CMD_3); - deferMessage(message); - transitionTo(mS2); - retVal = true; - break; - default: - // Any message we don't understand in this state invokes unhandledMessage - retVal = false; - break; - } - return retVal; - } - } - - class S1 extends State { - @Override - public void enter() { - tlog("S1.enter"); - } - @Override - public void exit() { - tlog("S1.exit"); - } - @Override - public boolean processMessage(Message message) { - tlog("S1.processMessage what=" + message.what); - if (message.what == CMD_1) { - // Transition to ourself to show that enter/exit is called - transitionTo(mS1); - return HANDLED; - } else { - // Let parent process all other messages - return NOT_HANDLED; - } - } - } - - class S2 extends State { - @Override - public void enter() { - tlog("S2.enter"); - } - @Override - public void exit() { - tlog("S2.exit"); - } - @Override - public boolean processMessage(Message message) { - boolean retVal; - tlog("S2.processMessage what=" + message.what); - switch(message.what) { - case(CMD_2): - sendMessage(CMD_4); - retVal = true; - break; - case(CMD_3): - deferMessage(message); - transitionTo(mP2); - retVal = true; - break; - default: - retVal = false; - break; - } - return retVal; - } - } - - class P2 extends State { - @Override - public void enter() { - tlog("P2.enter"); - sendMessage(CMD_5); - } - @Override - public void exit() { - tlog("P2.exit"); - } - @Override - public boolean processMessage(Message message) { - tlog("P2.processMessage what=" + message.what); - switch(message.what) { - case(CMD_3): - break; - case(CMD_4): - break; - case(CMD_5): - transitionToHaltingState(); - break; - } - return HANDLED; - } - } - - @Override - protected void onHalting() { - tlog("halting"); - synchronized (this) { - this.notifyAll(); - } - } - - P1 mP1 = new P1(); - S1 mS1 = new S1(); - S2 mS2 = new S2(); - P2 mP2 = new P2(); - } - - @MediumTest - public void testHsm1() throws Exception { - if (DBG) tlog("testHsm1 E"); - - Hsm1 sm = Hsm1.makeHsm1(); - - // Send messages - sm.sendMessage(Hsm1.CMD_1); - sm.sendMessage(Hsm1.CMD_2); - - synchronized (sm) { - // Wait for the last state machine to notify its done - try { - sm.wait(); - } catch (InterruptedException e) { - tloge("testHsm1: exception while waiting " + e.getMessage()); - } - } - - dumpLogRecs(sm); - - assertEquals(7, sm.getLogRecCount()); - - LogRec lr = sm.getLogRec(0); - assertEquals(Hsm1.CMD_1, lr.getWhat()); - assertEquals(sm.mS1, lr.getState()); - assertEquals(sm.mS1, lr.getOriginalState()); - - lr = sm.getLogRec(1); - assertEquals(Hsm1.CMD_2, lr.getWhat()); - assertEquals(sm.mP1, lr.getState()); - assertEquals(sm.mS1, lr.getOriginalState()); - - lr = sm.getLogRec(2); - assertEquals(Hsm1.CMD_2, lr.getWhat()); - assertEquals(sm.mS2, lr.getState()); - assertEquals(sm.mS2, lr.getOriginalState()); - - lr = sm.getLogRec(3); - assertEquals(Hsm1.CMD_3, lr.getWhat()); - assertEquals(sm.mS2, lr.getState()); - assertEquals(sm.mS2, lr.getOriginalState()); - - lr = sm.getLogRec(4); - assertEquals(Hsm1.CMD_3, lr.getWhat()); - assertEquals(sm.mP2, lr.getState()); - assertEquals(sm.mP2, lr.getOriginalState()); - - lr = sm.getLogRec(5); - assertEquals(Hsm1.CMD_4, lr.getWhat()); - assertEquals(sm.mP2, lr.getState()); - assertEquals(sm.mP2, lr.getOriginalState()); - - lr = sm.getLogRec(6); - assertEquals(Hsm1.CMD_5, lr.getWhat()); - assertEquals(sm.mP2, lr.getState()); - assertEquals(sm.mP2, lr.getOriginalState()); - - if (DBG) tlog("testStateMachineSharedThread X"); - } - - private static void tlog(String s) { - Log.d(TAG, s); - } - - private static void tloge(String s) { - Log.e(TAG, s); - } - - public void testDumpDoesNotThrowNpeAfterQuit() { - final Hsm1 sm = Hsm1.makeHsm1(); - sm.quitNow(); - final StringWriter stringWriter = new StringWriter(); - final PrintWriter printWriter = new PrintWriter(stringWriter); - sm.dump(null, printWriter, new String[0]); - } -} diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index e608a74a1cf8..fdc6e81646c4 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -464,6 +464,8 @@ applications that come with the platform <!-- Permission needed for CTS test - ConcurrencyTest#testP2pExternalApprover P2P external approver API sets require MANAGE_WIFI_NETWORK_SELECTION permission. --> <permission name="android.permission.MANAGE_WIFI_NETWORK_SELECTION" /> + <!-- Permission needed for CTS test - ConcurrencyTest#testP2pSetWfdInfo --> + <permission name="android.permission.CONFIGURE_WIFI_DISPLAY" /> <!-- Permission required for CTS test CarrierMessagingServiceWrapperTest --> <permission name="android.permission.BIND_CARRIER_SERVICES"/> <!-- Permission required for CTS test - MusicRecognitionManagerTest --> diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index c4d4261ba080..b44ccfbd341a 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -524,6 +524,8 @@ <!-- Permission needed for CTS test - ConcurrencyTest#testP2pExternalApprover P2P external approver API sets require MANAGE_WIFI_NETWORK_SELECTION permission. --> <uses-permission android:name="android.permission.MANAGE_WIFI_NETWORK_SELECTION" /> + <!-- Permission needed for CTS test - ConcurrencyTest#testP2pSetWfdInfo --> + <uses-permission android:name="android.permission.CONFIGURE_WIFI_DISPLAY" /> <!-- Permission required for CTS tests to enable/disable rate limiting toasts. --> <uses-permission android:name="android.permission.MANAGE_TOAST_RATE_LIMITING" /> diff --git a/services/core/java/com/android/server/integrity/OWNERS b/services/core/java/com/android/server/integrity/OWNERS index 55a4e409c767..33561fd728f9 100644 --- a/services/core/java/com/android/server/integrity/OWNERS +++ b/services/core/java/com/android/server/integrity/OWNERS @@ -2,4 +2,4 @@ omernebil@google.com khelmy@google.com mdchurchill@google.com sturla@google.com -songpan@google.com + diff --git a/services/core/java/com/android/server/media/BluetoothRouteProvider.java b/services/core/java/com/android/server/media/BluetoothRouteProvider.java index 91de9e559e13..b44ad922dd05 100644 --- a/services/core/java/com/android/server/media/BluetoothRouteProvider.java +++ b/services/core/java/com/android/server/media/BluetoothRouteProvider.java @@ -345,18 +345,14 @@ class BluetoothRouteProvider { private void addActiveRoute(BluetoothRouteInfo btRoute) { if (btRoute == null) { - if (DEBUG) { - Log.d(TAG, " btRoute is null"); - } + Slog.w(TAG, "addActiveRoute: btRoute is null"); return; } if (DEBUG) { Log.d(TAG, "Adding active route: " + btRoute.route); } if (mActiveRoutes.contains(btRoute)) { - if (DEBUG) { - Log.d(TAG, " btRoute is already added."); - } + Slog.w(TAG, "addActiveRoute: btRoute is already added."); return; } setRouteConnectionState(btRoute, STATE_CONNECTED); @@ -389,6 +385,12 @@ class BluetoothRouteProvider { private void addActiveDevices(BluetoothDevice device) { // Let the given device be the first active device BluetoothRouteInfo activeBtRoute = mBluetoothRoutes.get(device.getAddress()); + // This could happen if ACTION_ACTIVE_DEVICE_CHANGED is sent before + // ACTION_CONNECTION_STATE_CHANGED is sent. + if (activeBtRoute == null) { + activeBtRoute = createBluetoothRoute(device); + mBluetoothRoutes.put(device.getAddress(), activeBtRoute); + } addActiveRoute(activeBtRoute); // A bluetooth route with the same route ID should be added. diff --git a/services/core/java/com/android/server/power/stats/OWNERS b/services/core/java/com/android/server/power/stats/OWNERS new file mode 100644 index 000000000000..4068e2bc03b7 --- /dev/null +++ b/services/core/java/com/android/server/power/stats/OWNERS @@ -0,0 +1 @@ +include /BATTERY_STATS_OWNERS diff --git a/telephony/java/android/telephony/AccessNetworkUtils.java b/telephony/java/android/telephony/AccessNetworkUtils.java index b0f207cd9ed3..b5d97abdd3eb 100644 --- a/telephony/java/android/telephony/AccessNetworkUtils.java +++ b/telephony/java/android/telephony/AccessNetworkUtils.java @@ -4,8 +4,8 @@ import static android.telephony.ServiceState.DUPLEX_MODE_FDD; import static android.telephony.ServiceState.DUPLEX_MODE_TDD; import static android.telephony.ServiceState.DUPLEX_MODE_UNKNOWN; -import android.telephony.AccessNetworkConstants.EutranBand; import android.telephony.AccessNetworkConstants.EutranBandArfcnFrequency; +import android.telephony.AccessNetworkConstants.EutranBand; import android.telephony.AccessNetworkConstants.GeranBand; import android.telephony.AccessNetworkConstants.GeranBandArfcnFrequency; import android.telephony.AccessNetworkConstants.NgranArfcnFrequency; @@ -13,6 +13,7 @@ import android.telephony.AccessNetworkConstants.NgranBands; import android.telephony.AccessNetworkConstants.UtranBand; import android.telephony.AccessNetworkConstants.UtranBandArfcnFrequency; import android.telephony.ServiceState.DuplexMode; +import android.util.Log; import java.util.Arrays; import java.util.HashSet; @@ -231,110 +232,6 @@ public class AccessNetworkUtils { } /** - * Gets the NR Operating band for a given downlink NRARFCN. - * - * <p>See 3GPP TS 38.104 Table 5.2-1 NR operating bands in FR1 and - * Table 5.2-2 NR operating bands in FR2 - * - * @param nrarfcn The downlink NRARFCN - * @return Operating band number, or {@link #INVALID_BAND} if no corresponding band exists - */ - public static int getOperatingBandForNrarfcn(int nrarfcn) { - if (nrarfcn >= 2110 && nrarfcn <= 2170) { - return NgranBands.BAND_1; - } else if (nrarfcn >= 1930 && nrarfcn <= 1990) { - return NgranBands.BAND_2; - } else if (nrarfcn >= 1805 && nrarfcn <= 1880) { - return NgranBands.BAND_3; - } else if (nrarfcn >= 869 && nrarfcn <= 894) { - return NgranBands.BAND_5; - } else if (nrarfcn >= 2620 && nrarfcn <= 2690) { - return NgranBands.BAND_7; - } else if (nrarfcn >= 925 && nrarfcn <= 960) { - return NgranBands.BAND_8; - } else if (nrarfcn >= 729 && nrarfcn <= 746) { - return NgranBands.BAND_12; - } else if (nrarfcn >= 758 && nrarfcn <= 768) { - return NgranBands.BAND_14; - } else if (nrarfcn >= 860 && nrarfcn <= 875) { - return NgranBands.BAND_18; - } else if (nrarfcn >= 791 && nrarfcn <= 821) { - return NgranBands.BAND_20; - } else if (nrarfcn >= 1930 && nrarfcn <= 1995) { - return NgranBands.BAND_25; - } else if (nrarfcn >= 859 && nrarfcn <= 894) { - return NgranBands.BAND_26; - } else if (nrarfcn >= 758 && nrarfcn <= 803) { - return NgranBands.BAND_28; - } else if (nrarfcn >= 717 && nrarfcn <= 728) { - return NgranBands.BAND_29; - } else if (nrarfcn >= 2350 && nrarfcn <= 2360) { - return NgranBands.BAND_30; - } else if (nrarfcn >= 2010 && nrarfcn <= 2025) { - return NgranBands.BAND_34; - } else if (nrarfcn >= 2570 && nrarfcn <= 2620) { - return NgranBands.BAND_38; - } else if (nrarfcn >= 1880 && nrarfcn <= 1920) { - return NgranBands.BAND_39; - } else if (nrarfcn >= 2300 && nrarfcn <= 2400) { - return NgranBands.BAND_40; - } else if (nrarfcn >= 2496 && nrarfcn <= 2690) { - return NgranBands.BAND_41; - } else if (nrarfcn >= 5150 && nrarfcn <= 5925) { - return NgranBands.BAND_46; - } else if (nrarfcn >= 3550 && nrarfcn <= 3700) { - return NgranBands.BAND_48; - } else if (nrarfcn >= 1432 && nrarfcn <= 1517) { - return NgranBands.BAND_50; - } else if (nrarfcn >= 1427 && nrarfcn <= 1432) { - return NgranBands.BAND_51; - } else if (nrarfcn >= 2483 && nrarfcn <= 2495) { - return NgranBands.BAND_53; - } else if (nrarfcn >= 2110 && nrarfcn <= 2200) { - return NgranBands.BAND_65; // BAND_66 has the same channels - } else if (nrarfcn >= 1995 && nrarfcn <= 2020) { - return NgranBands.BAND_70; - } else if (nrarfcn >= 617 && nrarfcn <= 652) { - return NgranBands.BAND_71; - } else if (nrarfcn >= 1475 && nrarfcn <= 1518) { - return NgranBands.BAND_74; - } else if (nrarfcn >= 1432 && nrarfcn <= 1517) { - return NgranBands.BAND_75; - } else if (nrarfcn >= 1427 && nrarfcn <= 1432) { - return NgranBands.BAND_76; - } else if (nrarfcn >= 3300 && nrarfcn <= 4200) { - return NgranBands.BAND_77; - } else if (nrarfcn >= 3300 && nrarfcn <= 3800) { - return NgranBands.BAND_78; - } else if (nrarfcn >= 4400 && nrarfcn <= 5000) { - return NgranBands.BAND_79; - } else if (nrarfcn >= 2496 && nrarfcn <= 2690) { - return NgranBands.BAND_90; - } else if (nrarfcn >= 1427 && nrarfcn <= 1432) { - return NgranBands.BAND_91; - } else if (nrarfcn >= 1427 && nrarfcn <= 1432) { - return NgranBands.BAND_92; - } else if (nrarfcn >= 1432 && nrarfcn <= 1517) { - return NgranBands.BAND_93; - } else if (nrarfcn >= 1427 && nrarfcn <= 1432) { - return NgranBands.BAND_94; - } else if (nrarfcn >= 1432 && nrarfcn <= 1517) { - return NgranBands.BAND_94; - } else if (nrarfcn >= 5925 && nrarfcn <= 7125) { - return NgranBands.BAND_96; - } else if (nrarfcn >= 26500 && nrarfcn <= 29500) { - return NgranBands.BAND_257; - } else if (nrarfcn >= 24250 && nrarfcn <= 27500) { - return NgranBands.BAND_258; - } else if (nrarfcn >= 37000 && nrarfcn <= 40000) { - return NgranBands.BAND_260; - } else if (nrarfcn >= 27500 && nrarfcn <= 28350) { - return NgranBands.BAND_261; - } - return INVALID_BAND; - } - - /** * Gets the GERAN Operating band for a given ARFCN. * * <p>See 3GPP TS 45.005 clause 2 for calculation. |