CustomTabsService.java revision 08889acbc842c73b64f94a761910154d9d42ee4c
1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.support.customtabs;
18
19import android.app.Service;
20import android.content.Intent;
21import android.net.Uri;
22import android.os.Bundle;
23import android.os.IBinder;
24import android.os.IBinder.DeathRecipient;
25import android.os.RemoteException;
26import android.util.ArrayMap;
27
28import java.util.List;
29import java.util.Map;
30import java.util.NoSuchElementException;
31
32/**
33 * Abstract service class for implementing Custom Tabs related functionality. The service should
34 * be responding to the action ACTION_CUSTOM_TABS_CONNECTION. This class should be used by
35 * implementers that want to provide Custom Tabs functionality, not by clients that want to launch
36 * Custom Tabs.
37 */
38 public abstract class CustomTabsService extends Service {
39     /**
40      * The Intent action that a CustomTabsService must respond to.
41      */
42     public static final String ACTION_CUSTOM_TABS_CONNECTION =
43             "android.support.customtabs.action.CustomTabsService";
44
45     /**
46     * For {@link CustomTabsService#mayLaunchUrl(long, Uri, Bundle, List)} calls that wants to
47     * specify more than one url, this key can be used with
48     * {@link Bundle#putParcelable(String, android.os.Parcelable)}
49     * to insert a new url to each bundle inside list of bundles.
50      */
51     public static final String KEY_URL =
52             "android.support.customtabs.otherurls.URL";
53
54     private Map<IBinder, DeathRecipient> mDeathRecipientMap = new ArrayMap<>();
55
56     private ICustomTabsService.Stub mBinder = new ICustomTabsService.Stub() {
57
58         @Override
59         public boolean warmup(long flags) {
60             return CustomTabsService.this.warmup(flags);
61         }
62
63         @Override
64         public boolean newSession(ICustomTabsCallback callback) {
65             final CustomTabsSessionToken sessionToken = new CustomTabsSessionToken(callback);
66             try {
67                 DeathRecipient deathRecipient = new IBinder.DeathRecipient() {
68                     @Override
69                     public void binderDied() {
70                         cleanUpSession(sessionToken);
71                     }
72                 };
73                 synchronized (mDeathRecipientMap) {
74                     callback.asBinder().linkToDeath(deathRecipient, 0);
75                     mDeathRecipientMap.put(callback.asBinder(), deathRecipient);
76                 }
77                 return CustomTabsService.this.newSession(sessionToken);
78             } catch (RemoteException e) {
79                 return false;
80             }
81         }
82
83         @Override
84         public boolean mayLaunchUrl(ICustomTabsCallback callback, Uri url,
85                         Bundle extras, List<Bundle> otherLikelyBundles) {
86             return CustomTabsService.this.mayLaunchUrl(
87                     new CustomTabsSessionToken(callback), url, extras, otherLikelyBundles);
88         }
89     };
90
91     @Override
92     public IBinder onBind(Intent intent) {
93         return mBinder;
94     }
95
96     /**
97      * Called when the client side {@link IBinder} for this {@link CustomTabsSessionToken} is dead.
98      * Can also be used to clean up {@link DeathRecipient} instances allocated for the given token.
99      * @param sessionToken The session token for which the {@link DeathRecipient} call has been
100      *                     received.
101      * @return Whether the clean up was successful. Multiple calls with two tokens holdings the
102      *         same binder will return false.
103      */
104     protected boolean cleanUpSession(CustomTabsSessionToken sessionToken) {
105         try {
106             synchronized (mDeathRecipientMap) {
107                IBinder binder = sessionToken.getCallbackBinder();
108                DeathRecipient deathRecipient =
109                        mDeathRecipientMap.get(binder);
110                binder.unlinkToDeath(deathRecipient, 0);
111                mDeathRecipientMap.remove(binder);
112            }
113         } catch (NoSuchElementException e) {
114             return false;
115         }
116         return true;
117     }
118
119     /**
120      * Warms up the browser process asynchronously.
121      *
122      * @param flags Reserved for future use.
123      * @return      Whether warmup was/had been completed successfully. Multiple successful
124      *              calls will return true.
125      */
126     protected abstract boolean warmup(long flags);
127
128     /**
129      * Creates a new session through an ICustomTabsService with the optional callback. This session
130      * can be used to associate any related communication through the service with an intent and
131      * then later with a Custom Tab. The client can then send later service calls or intents to
132      * through same session-intent-Custom Tab association.
133      * @param sessionToken Session token to be used as a unique identifier. This also has access
134      *                     to the {@link CustomTabsCallback} passed from the client side through
135      *                     {@link CustomTabsSessionToken#getCallback()}.
136      * @return             Whether a new session was successfully created.
137      */
138     protected abstract boolean newSession(CustomTabsSessionToken sessionToken);
139
140     /**
141      * Tells the browser of a likely future navigation to a URL.
142      *
143      * The method {@link CustomTabsService#warmup(long)} has to be called beforehand.
144      * The most likely URL has to be specified explicitly. Optionally, a list of
145      * other likely URLs can be provided. They are treated as less likely than
146      * the first one, and have to be sorted in decreasing priority order. These
147      * additional URLs may be ignored.
148      * All previous calls to this method will be deprioritized.
149      *
150      * @param sessionToken       The unique identifier for the session. Can not be null.
151      * @param url                Most likely URL.
152      * @param extras             Reserved for future use.
153      * @param otherLikelyBundles Other likely destinations, sorted in decreasing
154      *                           likelihood order. Each Bundle has to provide a url.
155      * @return                   Whether the call was successful.
156      */
157     protected abstract boolean mayLaunchUrl(CustomTabsSessionToken sessionToken, Uri url,
158             Bundle extras, List<Bundle> otherLikelyBundles);
159}
160