Android review Notes directory
- Chatter task stack, return stack and lifecycle
- The life cycle of chatter activities
- Grilled steak a Context
- Why not display Dialog using Application Context?
In this paper, a permanent update address: xiaozhuanlan.com/topic/39581…
directory
- Why not display Dialog using Application Context?
- Who created the Token?
- How did WMS get the Token?
- How does WMS verify tokens?
Why not display Dialog using Application Context?
There is a question left in the previous article about scraping the Context:
Why not display Dialog using Application Context?
Write a simple test code like this:
Dialog dialog = new Dialog(getApplicationContext());
dialog.show();
Copy the code
At runtime, you get an error like this:
Caused by: android.view.WindowManager$BadTokenException: Unable to add window -- token null is not valid; is your activity running?
at android.view.ViewRootImpl.setView(ViewRootImpl.java:951)
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:387)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:96)
at android.app.Dialog.show(Dialog.java:344)
at luyao.android.context.ContextActivity.showDialog(ContextActivity.java:31)
Copy the code
Attach () = attach(); attach() = attach();
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window, ActivityConfigCallback activityConfigCallback) {
/ / callback attachBaseContext ()attachBaseContext(context); ./ / create PhoneWindow
mWindow = new PhoneWindow(this, window, activityConfigCallback); .// The second argument is mTokenmWindow.setWindowManager( (WindowManager)context.getSystemService(Context.WINDOW_SERVICE), mToken, mComponent.flattenToString(), (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) ! =0); . }Copy the code
After the Activity is created, the attach() method is called, which does several things:
- The PhoneWindow object is created
mWondow
- Bind to the current window
mToken
- .
The IBinder object mToken here is important. Binder object that can be passed between APP and system_server processes. As with Token, it can also be regarded as a special Token used to identify the Window, which can be checked when performing view operations on the Window.
Therefore, the Window/WMS corresponding to the Activity holds this mToken. We can assume that the Application Context did not instantiate a similar token when creating the Dialog.
Back in the Dialog constructor,
Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
......
/ / get WindowManager
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
final Window w = newPhoneWindow(mContext); mWindow = w; . }Copy the code
Call getSystemService(Context.window_service) based on the incoming Context to get the WindowManager object mWindowManager, The dialog will eventually be displayed via mWindowManager.addWindow().
If the Context passed in is Activity, the mWindowManager object created in the activity.attach () method is returned, at which point the mToken is already bound.
> Activity.java
@Override
public Object getSystemService(@ServiceName @NonNull String name) {...if (WINDOW_SERVICE.equals(name)) {
return mWindowManager; // instantiated in the attach() method
} else if (SEARCH_SERVICE.equals(name)) {
ensureSearchManager();
return mSearchManager;
}
return super.getSystemService(name);
}
Copy the code
If the Context is passed in as Application, the method of the parent ContextImpl class is called.
@Override
public Object getSystemService(String name) {
return SystemServiceRegistry.getSystemService(this, name);
}
Copy the code
SystemServiceRegistry. GetSystemService (this name), it will have to complete and caching system initialization services, did not carry any Token.
registerService(Context.WINDOW_SERVICE, WindowManager.class,
new CachedServiceFetcher<WindowManager>() {
@Override
public WindowManager createService(ContextImpl ctx) {
return new WindowManagerImpl(ctx);
}});
Copy the code
Therefore, Android does not allow normal dialogs to be created and displayed in a Context other than an Activity. By ordinary, I mean the ordinary Dialog in the sample code at the beginning of this article, not Toast, System Dialog, and so on. For the sake of security, The Android system does not want the Dialog to pop up even after the App enters the background, which will lead to the scene of pop-ups in other apps, causing certain security risks. Although the Activity through the Dialog Theme can still fulfill this requirement, Google is also tightening restrictions on background Activity launches.
At this point, the question seems to have been answered. But in fact, we still have no idea about the whole Token process. Consider the following questions.
- When and where was mToken created?
- How did WMS get the mToken?
- How does WMS verify tokens?
- .
Only after truly mastering these problems can a complete closed loop of knowledge be formed, which is accompanied by an inescapable, boring Read the Fucking AOSP.
Who created the Token?
Let’s take a look at what kind of class Token is.
> ActivityRecord.java
static class Token extends IApplicationToken.Stub {
private final WeakReference<ActivityRecord> weakActivity;
private final String name;
Token(ActivityRecord activity, Intent intent) {
weakActivity = newWeakReference<>(activity); name = intent.getComponent().flattenToShortString(); }... }Copy the code
Token is a static inner class of ActivityRecord that holds a weak reference to an external ActivityRecord. Stub, inherited from iApplicationToken. Stub, is a Binder object. It is initialized in the constructor of ActivityRecord.
> ActivityRecord.java
ActivityRecord(ActivityManagerService _service, ProcessRecord _caller, int _launchedFromPid,
int _launchedFromUid, String _launchedFromPackage, Intent _intent, String _resolvedType,
ActivityInfo aInfo, Configuration _configuration,
ActivityRecord _resultTo, String _resultWho, int _reqCode,
boolean _componentSpecified, boolean _rootVoiceInteraction,
ActivityStackSupervisor supervisor, ActivityOptions options,
ActivityRecord sourceRecord) {
service = _service;
// Initialize the appToken
appToken = new Token(this, _intent); . }Copy the code
An ActivtyRecord represents an Activity instance that contains all information about the Activity. In the process of the Activity start, when you perform to ActivityStarter. StartActivity (), will create a startup ActivityRecord object, also indirectly created Token objects.
> ActivityStarter.java
private int startActivity(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,
String callingPackage, int realCallingPid, int realCallingUid, int startFlags,
SafeActivityOptions options,
boolean ignoreTargetSecurity, boolean componentSpecified, ActivityRecord[] outActivity,
TaskRecord inTask, boolean allowPendingRemoteAnimationRegistryLookup,
PendingIntentRecord originatingPendingIntent) {...// Build ActivityRecord, which initializes tokens
ActivityRecord r = newActivityRecord(mService, callerApp, callingPid, callingUid, callingPackage, intent, resolvedType, aInfo, mService.getGlobalConfiguration(), resultRecord, resultWho, requestCode, componentSpecified, voiceSession ! =null, mSupervisor, checkedOptions, sourceRecord); .return startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags,
true /* doResume */, checkedOptions, inTask, outActivity);
}
Copy the code
At this point, activityRecord.appToken has been assigned a value. So the Token is created in the startActivity process of AMS. But the Token verification obviously takes place in WMS, so AMS also has to submit the Token to WMS.
How did WMS get the Token?
Will continue with startActivity () the last call to ActivityStack startActivityLocked (), this method is the key to give to the WMS Token.
> ActivityStack.java
void startActivityLocked(ActivityRecord r, ActivityRecord focusedTopActivity,
boolean newTask, boolean keepCurTransition, ActivityOptions options) {...if (r.getWindowContainerController() == null) {
/ / create AppWindowContainerController object that contains the token objectr.createWindowContainer(); . }Copy the code
The rest of the code is omitted, focusing on R.createWindowContainer (), where r is the ActivityRecord object created initially.
> ActivityRecord.java
void createWindowContainer(a) {
if(mWindowContainerController ! =null) {
throw new IllegalArgumentException("Window container=" + mWindowContainerController
+ " already created for r=" + this);
}
inHistory = true;
finalTaskWindowContainerController taskController = task.getWindowContainerController(); .CreateAppWindow () is called in the constructor to create the AppWindowToken object
mWindowContainerController = new AppWindowContainerController(taskController, appToken,
this, Integer.MAX_VALUE /* add on top */, info.screenOrientation, fullscreen, (info.flags & FLAG_SHOW_FOR_ALL_USERS) ! =0, info.configChanges, task.voiceSession ! =null, mLaunchTaskBehind, isAlwaysFocusable(),
appInfo.targetSdkVersion, mRotationAnimationHint,
ActivityManagerService.getInputDispatchingTimeoutLocked(this) * 1000000L);
task.addActivityToTop(this); . }Copy the code
In the constructor of AppWindowContainerController incoming appToken has initialized before.
> AppWindowContainerController.java
public AppWindowContainerController(TaskWindowContainerController taskController,
IApplicationToken token, AppWindowContainerListener listener, int index,
int requestedOrientation, boolean fullscreen, boolean showForAllUsers, int configChanges,
boolean voiceInteraction, boolean launchTaskBehind, boolean alwaysFocusable,
int targetSdkVersion, int rotationAnimationHint, long inputDispatchingTimeoutNanos,
WindowManagerService service) {
super(listener, service);
mHandler = new H(service.mH.getLooper());
mToken = token;
synchronized(mWindowMap) {
......
atoken = createAppWindow(mService, token, voiceInteraction, task.getDisplayContent(),
inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdkVersion,
requestedOrientation, rotationAnimationHint, configChanges, launchTaskBehind,
alwaysFocusable, this); . }}Copy the code
The AppWindowToken object is created in the createAppWindow() method. Note the token argument passed in.
> AppWindowContainerController.java
AppWindowToken createAppWindow(WindowManagerService service, IApplicationToken token,
boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos,
boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation,
int rotationAnimationHint, int configChanges, boolean launchTaskBehind,
boolean alwaysFocusable, AppWindowContainerController controller) {
return new AppWindowToken(service, token, voiceInteraction, dc,
inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk, orientation,
rotationAnimationHint, configChanges, launchTaskBehind, alwaysFocusable,
controller);
}
Copy the code
> AppWindowToken.java
AppWindowToken(WindowManagerService service, IApplicationToken token, boolean voiceInteraction,
DisplayContent dc, boolean fillsParent) {
// The parent is WindowToken
super(service, token ! =null ? token.asBinder() : null, TYPE_APPLICATION, true, dc,
false /* ownerCanManageAppTokens */);
appToken = token;
mVoiceInteraction = voiceInteraction;
mFillsParent = fillsParent;
mInputApplicationHandle = new InputApplicationHandle(this);
}
Copy the code
The parent constructor is called, and the parent of AppWindowToken is WindowToken.
> WindowToken.java
WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty,
DisplayContent dc, boolean ownerCanManageAppTokens, boolean roundedCornerOverlay) {
super(service);
token = _token;
windowType = type;
mPersistOnEmpty = persistOnEmpty;
mOwnerCanManageAppTokens = ownerCanManageAppTokens;
mRoundedCornerOverlay = roundedCornerOverlay;
// Then follow
onDisplayChanged(dc);
}
Copy the code
> WindowToken.java
void onDisplayChanged(DisplayContent dc) {
/ / call DisplayContent. ReParentWindowToken ()
dc.reParentWindowToken(this); mDisplayContent = dc; . }Copy the code
> DisplayContent.java
void reParentWindowToken(WindowToken token) {... addWindowToken(token.token, token); }private void addWindowToken(IBinder binder, WindowToken token) {...// mTokenMap is a HashMap
,>mTokenMap.put(binder, token); . }Copy the code
MTokenMap is a HashMap
object that holds a WindowToken and its Token information. Since we started the Activity from AMS all the way to here, we have actually come to the logic of WMS. Both AMS and WMS run in the system_server process and have no binder calls. AMS passes the Token to WMS following the above call chain.
Here is a clear flow chart summarizing the whole process of Token transfer from AMS to WMS:
How does WMS verify tokens?
In fact, has been very tangled source code parsing class article should how to write. Thinking alone is not enough for many people; Source code said much, the article appears dull and boring. If you have any good suggestions, you can talk about them in the comments section.
This piece of source code I am not in the article bit by bit after. You can look at the following flow chart to chew their own source code. From the Dialog. The show (), and finally go to WindowManagerService. AddwWindow ().
The Token is checked in the WMS.addWindow() method.
public int addWindow(Session session, IWindow client, int seq,
LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel) {... AppWindowToken atoken =null;
final booleanhasParent = parentWindow ! =null;
/ / get WindowToken
WindowToken token = displayContent.getWindowToken(
hasParent ? parentWindow.mAttrs.token : attrs.token);
if (token == null) {
if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
Slog.w(TAG_WM, "Attempted to add application window with unknown token "
+ attrs.token + ". Aborting.");
returnWindowManagerGlobal.ADD_BAD_APP_TOKEN; }... }else{... }... }Copy the code
Through DisplayContent. GetWindowToken () method to obtain WindowToken object, will work to a series of calibration. And if you look at DisplayContent, you can imagine that it’s going to be evaluated from the mTokenMap set mentioned above. Let’s take a look at the source code implementation.
> DisplayContent.java
WindowToken getWindowToken(IBinder binder) {
return mTokenMap.get(binder);
}
Copy the code
Yes, it does come directly from the hash table mTokenMap. At this point, the whole process is done.
- When AMS starts an Activity, it builds an ActivityRecord object that represents the Activity information and instantiates the Token object in its constructor
- AMS on the next step after, will use to create a Token build AppWindowContainerController object, eventually will be stored in Token mTokenMap of WMS
- When WMS adds Window, it verifies the Token of the current Window object
The last
We have come to the fourth chapter. Every time I think about the topic of the next article, I will think for a long time. If you have any good interview questions, you can comment on them in the comment area, which will become my writing material.
I also maintain a shared graphite document,
Shimo. Im/docs/jYhDGH…
You can contribute your own questions and, if you are able, leave your own answers.