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