• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed urnder 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 package android.net;
17 
18 import android.annotation.NonNull;
19 import android.annotation.RequiresPermission;
20 import android.annotation.SystemApi;
21 import android.annotation.TargetApi;
22 import android.os.Binder;
23 import android.os.Build;
24 import android.os.IBinder;
25 import android.os.OutcomeReceiver;
26 import android.os.Parcel;
27 import android.os.Parcelable;
28 import android.os.RemoteException;
29 import android.os.ServiceSpecificException;
30 import android.system.OsConstants;
31 
32 import java.util.Objects;
33 import java.util.concurrent.Executor;
34 
35 /**
36  * A class allowing apps handling the {@link ConnectivityManager#ACTION_CAPTIVE_PORTAL_SIGN_IN}
37  * activity to indicate to the system different outcomes of captive portal sign in.  This class is
38  * passed as an extra named {@link ConnectivityManager#EXTRA_CAPTIVE_PORTAL} with the
39  * {@code ACTION_CAPTIVE_PORTAL_SIGN_IN} activity.
40  */
41 public class CaptivePortal implements Parcelable {
42     /**
43      * Response code from the captive portal application, indicating that the portal was dismissed
44      * and the network should be re-validated.
45      * @see ICaptivePortal#appResponse(int)
46      * @see android.net.INetworkMonitor#notifyCaptivePortalAppFinished(int)
47      * @hide
48      */
49     @SystemApi
50     public static final int APP_RETURN_DISMISSED    = 0;
51     /**
52      * Response code from the captive portal application, indicating that the user did not login and
53      * does not want to use the captive portal network.
54      * @see ICaptivePortal#appResponse(int)
55      * @see android.net.INetworkMonitor#notifyCaptivePortalAppFinished(int)
56      * @hide
57      */
58     @SystemApi
59     public static final int APP_RETURN_UNWANTED     = 1;
60     /**
61      * Response code from the captive portal application, indicating that the user does not wish to
62      * login but wants to use the captive portal network as-is.
63      * @see ICaptivePortal#appResponse(int)
64      * @see android.net.INetworkMonitor#notifyCaptivePortalAppFinished(int)
65      * @hide
66      */
67     @SystemApi
68     public static final int APP_RETURN_WANTED_AS_IS = 2;
69     /** Event offset of request codes from captive portal application. */
70     private static final int APP_REQUEST_BASE = 100;
71     /**
72      * Request code from the captive portal application, indicating that the network condition may
73      * have changed and the network should be re-validated.
74      * @see ICaptivePortal#appRequest(int)
75      * @see android.net.INetworkMonitor#forceReevaluation(int)
76      * @hide
77      */
78     @SystemApi
79     public static final int APP_REQUEST_REEVALUATION_REQUIRED = APP_REQUEST_BASE + 0;
80 
81     /**
82      * Binder object used for tracking the lifetime of the process, so CS can perform cleanup if
83      * the CaptivePortal app dies. This binder is not parcelled as part of this object. It is
84      * created in the client process and sent to the server by setDelegateUid so that the server
85      * can use it to register a death recipient.
86      *
87      */
88     private final Binder mLifetimeBinder = new Binder();
89 
90     private final IBinder mBinder;
91 
92     /** @hide */
CaptivePortal(@onNull IBinder binder)93     public CaptivePortal(@NonNull IBinder binder) {
94         mBinder = binder;
95     }
96 
97     @Override
describeContents()98     public int describeContents() {
99         return 0;
100     }
101 
102     @Override
writeToParcel(Parcel out, int flags)103     public void writeToParcel(Parcel out, int flags) {
104         out.writeStrongBinder(mBinder);
105     }
106 
107     public static final @android.annotation.NonNull Parcelable.Creator<CaptivePortal> CREATOR
108             = new Parcelable.Creator<CaptivePortal>() {
109         @Override
110         public CaptivePortal createFromParcel(Parcel in) {
111             return new CaptivePortal(in.readStrongBinder());
112         }
113 
114         @Override
115         public CaptivePortal[] newArray(int size) {
116             return new CaptivePortal[size];
117         }
118     };
119 
120     /**
121      * Indicate to the system that the captive portal has been
122      * dismissed.  In response the framework will re-evaluate the network's
123      * connectivity and might take further action thereafter.
124      */
reportCaptivePortalDismissed()125     public void reportCaptivePortalDismissed() {
126         try {
127             ICaptivePortal.Stub.asInterface(mBinder).appResponse(APP_RETURN_DISMISSED);
128         } catch (RemoteException e) {
129         }
130     }
131 
132     /**
133      * Indicate to the system that the user does not want to pursue signing in to the
134      * captive portal and the system should continue to prefer other networks
135      * without captive portals for use as the default active data network.  The
136      * system will not retest the network for a captive portal so as to avoid
137      * disturbing the user with further sign in to network notifications.
138      */
ignoreNetwork()139     public void ignoreNetwork() {
140         try {
141             ICaptivePortal.Stub.asInterface(mBinder).appResponse(APP_RETURN_UNWANTED);
142         } catch (RemoteException e) {
143         }
144     }
145 
146     /**
147      * Indicate to the system the user wants to use this network as is, even though
148      * the captive portal is still in place.  The system will treat the network
149      * as if it did not have a captive portal when selecting the network to use
150      * as the default active data network. This may result in this network
151      * becoming the default active data network, which could disrupt network
152      * connectivity for apps because the captive portal is still in place.
153      * @hide
154      */
155     @SystemApi
useNetwork()156     public void useNetwork() {
157         try {
158             ICaptivePortal.Stub.asInterface(mBinder).appResponse(APP_RETURN_WANTED_AS_IS);
159         } catch (RemoteException e) {
160         }
161     }
162 
163     /**
164      * Request that the system reevaluates the captive portal status.
165      * @hide
166      */
167     @SystemApi
168     @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
reevaluateNetwork()169     public void reevaluateNetwork() {
170         try {
171             ICaptivePortal.Stub.asInterface(mBinder).appRequest(APP_REQUEST_REEVALUATION_REQUIRED);
172         } catch (RemoteException e) {
173         }
174     }
175 
176     /**
177      * Log a captive portal login event.
178      * @param eventId one of the CAPTIVE_PORTAL_LOGIN_* constants in metrics_constants.proto.
179      * @param packageName captive portal application package name.
180      * @hide
181      * @deprecated The event will not be logged in Android S and above. The
182      * caller is migrating to statsd.
183      */
184     @Deprecated
185     @SystemApi
logEvent(int eventId, @NonNull String packageName)186     public void logEvent(int eventId, @NonNull String packageName) {
187     }
188 
189     /**
190      * Sets the UID of the app that is allowed to perform network traffic for captive
191      * portal login.
192      *
193      * This app will be allowed to communicate directly on the captive
194      * portal by binding to the {@link android.net.Network} extra passed in the
195      * ACTION_CAPTIVE_PORTAL_SIGN_IN broadcast that contained this object.
196      *
197      * Communication will bypass network access restrictions such as VPNs and
198      * Private DNS settings, so the delegated UID must be trusted to ensure that only
199      * traffic intended for captive portal login binds to that network.
200      *
201      * By default, no UID is delegated. The delegation can be cleared by calling
202      * this method again with {@link android.os.Process.INVALID_UID}. Only one UID can
203      * be delegated at any given time.
204      *
205      * The operation is asynchronous. The uid is only guaranteed to have access when
206      * the provided OutcomeReceiver is called.
207      *
208      * @hide
209      */
210     @RequiresPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK)
211     // OutcomeReceiver is not available on R, but the mainline version of this
212     // class is only available on S+.
213     @TargetApi(Build.VERSION_CODES.S)
setDelegateUid(int uid, @NonNull Executor executor, @NonNull final OutcomeReceiver<Void, ServiceSpecificException> receiver)214     public void setDelegateUid(int uid, @NonNull Executor executor,
215             @NonNull final OutcomeReceiver<Void, ServiceSpecificException> receiver) {
216         Objects.requireNonNull(executor);
217         Objects.requireNonNull(receiver);
218         try {
219             ICaptivePortal.Stub.asInterface(mBinder).setDelegateUid(
220                     uid,
221                     mLifetimeBinder,
222                     new IIntResultListener.Stub() {
223                         @Override
224                         public void onResult(int resultCode) {
225                             if (resultCode != 0) {
226                                 final String msg = "Fail to set the delegate UID " + uid
227                                         + ", error: " + OsConstants.errnoName(resultCode);
228                                 executor.execute(() -> {
229                                     receiver.onError(new ServiceSpecificException(resultCode, msg));
230                                 });
231                             } else {
232                                 executor.execute(() -> receiver.onResult(null));
233                             }
234                         }
235                     });
236         } catch (RemoteException e) {
237             throw e.rethrowFromSystemServer();
238         }
239     }
240 }
241