preface

If you don’t know about cookies, you may feel intimidated. Those of you who have done WebView development will probably be familiar with it. The Cookie of Android is managed by the system, and its feature is that it will be persisted into a DB file, which is saved in /data/data/{packageName}/app_webview/Cookies (different systems and different browsers may implement different, but generally the same). Generally, the login information of websites is saved by cookies. If the App also uses cookies for authentication, a Cookie synchronization mechanism needs to be established between WebView and App.

Although the authentication mechanism of Koala is not implemented by using cookies, we also encountered similar requirements. When opening a specific URL with WebView, the response of the URL will be written into the specified Cookie, and then the URL will go through a 302 redirect and open an App page after url interception. And bring the Cookie carried in the URL response to the App page.

Synchronize with the process Cookie

If App and WebView are in the same process, then implementation is relatively simple, can refer to this article, code will not be too much explanation, take okhttp as an example:

import android.webkit.CookieManager;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import okhttp3.Cookie;
import okhttp3.CookieJar;
import okhttp3.HttpUrl;

/**
 * Provides a synchronization point between the webview cookie store and okhttp3.OkHttpClient cookie store
 */
public final class WebviewCookieHandler implements CookieJar {
    private CookieManager webviewCookieManager = CookieManager.getInstance();

    @Override
    public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
        String urlString = url.toString();

        for (Cookie cookie : cookies) {
            webviewCookieManager.setCookie(urlString, cookie.toString());
        }
    }

    @Override
    public List<Cookie> loadForRequest(HttpUrl url) {
        String urlString = url.toString();
        String cookiesString = webviewCookieManager.getCookie(urlString);

        if(cookiesString ! = null && ! cookiesString.isEmpty()) { //We can split on the'; ' char as the cookie manager only returns cookies
            //that match the url and haven't expired, so the cookie attributes aren't included
            String[] cookieHeaders = cookiesString.split(";");
            List<Cookie> cookies = new ArrayList<>(cookieHeaders.length);

            for (String header : cookieHeaders) {
                cookies.add(Cookie.parse(url, header));
            }

            return cookies;
        }

        returnCollections.emptyList(); }}Copy the code

Code from gist.github.com/justinthoma… .

Multi-process Cookie synchronization

But if the App and WebView are in a different process, it’s not that simple. Since data is not shared between different processes, Cookie synchronization between processes becomes an issue. Subsequent tests found that multiple processes of the App shared the same Cookie file, but the Cookie data between processes could not be synchronized in real time. The problem we encountered was that when the WebView process accessed a URL that carried a specific Cookie, those cookies were not synchronized to the main process. Therefore, with layers of questions, we started the guess experiment of inter-process synchronization Cookie. Consider where Cookie data may be inconsistent between two processes (let’s assume App is in process A and WebView is in process B) :

  1. The WebView accesses A URL. After the WebView of process B writes Cookies, it does not write Cookies. Db is persisted immediately, so process A cannot read the latest Cookies.
  2. Since cookies are linked to WebView, you may need to create A WebView in process A to synchronize cookies between processes.
  3. Process A needs to be calledCookieManager.getInstance().setAcceptCookie(true)Ensure that process A can read cookies.
  4. The Cookie of process B may be invalid, causing process A to fail to read the Cookie (explained why this happens later).
  5. The Cookie files of process A and process B are different from each other. As A result, data cannot be synchronized.
  6. Process A creates the WebView and accesses the url of the same domain, and then flushes the Cookie that process B has persisted before.
  7. Cookies are managed by Cookie emanager, which is a singleton and may read Cookies. Db only once and then cache it in memory;

Let’s analyze each of the seven cases and test them conditionally. It should be noted that, in order not to affect the results of each experiment, it is necessary to empty the Cookie file under /data/data directory before loading the URL.

The first guess — Cookie persistence time

The WebView accesses A URL. After the WebView of process B writes Cookies, the Cookies. Db is not persisted immediately, causing process A to fail to read the latest Cookies.

When the WebView loads the URL, the server returns the Cookie that needs to be written. You can use Chrome Inspect to check. We can do a simple experiment on the timing of WebView Cookie persistence.

Experimental steps: 1. Use WebView to load URL; 2, after the completion of the loading (call WebViewClient. OnPageFinished ()), get a Cookie file, see if any written cookies.

Take https://m.baidu.com as an example. Before the WebView component is loaded, you can find a mobile phone with root experience and check that the app_webview directory does not exist in the /data/data/{packageName} directory.

After loading the URL, you can use Chrome Inspect to check the Cookie information, and m.baidu.com will generate the following Cookie:

If you visit the directory again, you can see that the app_webview directory already exists and a Cookie file has been generated. The Cookie has been generated and persisted when the WebView is opened for the first time and https://m.baidu.com is loaded.

To further verify, we export /data/data/{packageName}/app_webview/Cookies and check whether the above Cookie is included to verify whether the Cookie has been persisted.

The result is obvious — the Cookie is persisted almost immediately after the WebView loads the URL, which disproves our first guess.

Second guess — Cookie synchronization condition

Since cookies are linked to WebView, you may need to create A WebView in process A to synchronize cookies between processes.

As we know, WebView cookies are managed by the system [^1], and the WebView may perform certain operations on cookies during the instantiation process. If the WebView is not instantiated, is the Cookie not synced? Based on this conjecture, we conducted a second experiment.

Experimental steps: 1. After B process loads https://m.baidu.com, use CookieManager to check the Cookie of m.baidu.com in B process; 2. Process A instantiates the WebView, does not load it, and then uses CookieManager to check the Cookie of m.baidu.com in process A; 3. Process B uses WebView to load https://m.taobao.com again and check the Cookie of m.taobao.com in process B. 4. Process A instantiates the WebView again, does not load it, and checks the Cookie of m.taobao.com in process A.

We see an interesting phenomenon:

When the WebView of process A is instantiated for the first time, the Cookie written by process B can be retrieved. However, when process B writes other cookies again, the WebView of process A is not available. This process may indicate that persistent cookies are synchronized only when the WebView is instantiated for the first time. When the Cookie is updated again, no other process can read the updated Cookie data. The second conjecture is not true.

And the third hypothesis —setAcceptCookie(true)

A process need to invoke the CookieManager. GetInstance () setAcceptCookie (true) to ensure A process is able to read to the cookies.

Since cookies are required and it is unknown whether the process allows cookies by default, we can test by forcing the process to allow cookies. You can use the following code:

CookieManage.getInstance().setAcceptCookie(true);
Copy the code

Cookiemanage.getinstance ().setAcceptcookie (true); 2. Repeat the experimental steps of Guess 2 to observe the Cookie synchronization of process A and process B. Call Cookiemanage.getInstance ().setAcceptcookie (false) when Application starts; 4. Repeat the steps of Guess 2 again.

Regardless of whether cookies are allowed to record, the test result is the same as the result of guess 2. The graph is not attached, indicating that Cookie synchronization between processes is independent of whether cookies are allowed to record. The third conjecture is not true.

The fourth guess — Cookie failure problem

The Cookie of process B may be invalid, causing process A to fail to read the Cookie.

The Cookie of process B may be invalid, causing process A to fail to read the Cookie. The reason for this is that when we use Chrome Inspect to view the Cookie, it does display an expired time, such as the https://m.baidu.com we just visited,

There is a Cookie with the time of 2019-04-28T05:38:12.000z, but notice that the letter Z at the end of the time indicates the GMT+0 time zone in GMT/UTC [^2]. When converted to Beijing Time (GMT+8), it is 1:38 p.m.

Note The Cookie is valid, and process A may fail to access the Cookie. In addition, in the Android, even if the Cookie has failure, can also pass CookieManager. GetInstance () getCookie (url), and the method returns a string that contains the Cookie Expires fields. The fourth conjecture is not true.

Fifth guess – Cookie file process read

The Cookie files of process A and process B are different from each other. As A result, data cannot be synchronized.

The Cookie files of process A and process B are different from each other. As A result, data cannot be synchronized. After the above conjecture and experiment, it can be shown that this conjecture is not valid. If the Cookie files read by process are not the same, then process A cannot get the Cookie written by process B’s WebView after process B accesses https://m.baidu.com. The conclusion of Test two illustrates this point. In order to make the facts more convincing, let the experiment illustrate the point.

Procedure: 1. Process B visits https://m.baidu.com; 2. Save the last modification time of the Cookie file; 3. Process A can access https://m.baidu.com(or other URL again. 4. Check the last modification time of the Cookie file and compare it with step 2.

We visited https://m.baidu.com at 14:06 in process B and 14:08 in process A, and the results are as follows:

Note Different processes in the App use the same Cookie file to read and write data. The fifth conjecture is not true.

Sixth guess – cookies and process in the same domain access

Process A creates the WebView and accesses the url of the same domain, and overwrites the Cookie that process B has persisted before

According to the experimental results of the fifth guess, different processes use the same Cookie file for persistence. If both processes A and B allow cookies to be written, then Cookie overwriting may occur between processes. We can test that out.

Experimental steps: 1. Use B process WebView to open https://m.baidu.com and record the current Cookie file; 2. Use WebView of A process to open https://m.baidu.com and record the current Cookie file; 3. Compare the Cookie files of step 1 and Step 2.

https://m.baidu.com

https://m.baidu.com

As can be seen from the figure, the Cookie data after B accesses the URL is almost the same as the Cookie data after A accesses the URL, except for one column: last_access_UTC. We guess that this field represents the time when the Cookie was successfully read/written last time (no relevant documentation was found), but at least it indicates that the file of Cookies has been overwritten, that is to say, different processes in App may access the same domain, which may cause Cookie overwritten.

Even so, so far, there is no explanation for the failure of process B to obtain some cookies from process A.

Seventh guess — CookieManager’s pot

Cookies are managed through Cookie emanager, which is a singleton, and a single process may read Cookies. Db only once and then cache them in memory.

All operations related to cookies in Android are related to CookieManager. None of the above assumptions take into account CookieManager. CookieManager is a singleton that, once created, will not be destroyed unless the process is cleared. If CookieManager only reads the cookie.db file once when it is created, and uses the in-memory cache in preference for Cookie reading, then the above phenomenon can be explained. Let’s do experiments.

Experimental steps: 1, A process is not initialized CookieManager, using process B to access https://m.baidu.com, Cookie persistence, and then respectively in the initialization of A process CookieManager before and after, check the Cookie of A process; Then use process B to access https://m.taobao.com. After the Cookie is persisted, check the Cookie status of process A again. 2. If process A does not initialize CookieManager, use process B to access https://m.baidu.com and https://m.taobao.com. After the Cookie is persisted, initialize CookieManager of process A. And check the cookies of process A.

The results confirmed the conjecture! CookieManager cannot obtain the Cookie of M.baidu.com when it is not initialized. Once CookieManager is initialized, it can obtain the Cookie of M.baidu.com. But step 2 again explains that as long as the CookieManager is initialized, the Cookie of the process can no longer get Cookie information updated by other processes.

Conclusion of multi-process Cookie synchronization

At this point, the conjecture of the Cookie synchronization problem under the multi-process has been verified, and the conclusion can be drawn is that the Cookie acquisition between the multi-process is only related to the first initialization of CookieManager. Once the CookieManager instance is created, the process needs to restart to synchronize the Cookie between the processes.

Returning to the problem encountered in this article, now that the cause of the problem has been identified, there must be a solution. An imperfect scheme is to start the B process and load the URL, wait until the completion of the loading will jump to the App page when the notification of the main process initialization CookieManager, so that you can take the Cookie information specified in the URL. The disadvantage of this scheme is that when the url is accessed again and a new specified Cookie is written, it will not be immediately synchronized to the main process until the App restarts the main process. Another solution is to put both the WebView and App in the main process. In this paper, the second scheme is adopted because the multi-process Cookie synchronization scheme cannot be perfectly solved.

Refer to the link

  • www.cnblogs.com/zhangyoushu…
  • Stackoverflow.com/questions/1…