1 /* 2 * Copyright (C) 2018 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 17 package com.android.server.wifi.hotspot2.soap.command; 18 19 import android.annotation.NonNull; 20 import android.util.Log; 21 22 import org.ksoap2.serialization.PropertyInfo; 23 import org.ksoap2.serialization.SoapObject; 24 25 import java.util.HashMap; 26 import java.util.Map; 27 import java.util.Objects; 28 29 /** 30 * Commands that the mobile device is being requested to execute, which is defined in SPP 31 * (Subscription Provisioning Protocol). 32 * 33 * For the details, refer to A.3.2 of Hotspot 2.0 rel2 technical specification. 34 */ 35 public class SppCommand { 36 private static final String TAG = "PasspointSppCommand"; 37 private int mSppCommandId; 38 private int mExecCommandId = -1; 39 private SppCommandData mCommandData; 40 41 /** 42 * Marker interface to indicate data used for a SPP(Subscription Provisioning Protocol) command 43 */ 44 public interface SppCommandData { 45 46 } 47 48 /** 49 * Commands embedded in sppPostDevDataResponse message for client to take an action. 50 */ 51 public class CommandId { 52 public static final int EXEC = 0; 53 public static final int ADD_MO = 1; 54 public static final int UPDATE_NODE = 2; 55 public static final int NO_MO_UPDATE = 3; 56 } 57 58 private static final Map<String, Integer> sCommands = new HashMap<>(); 59 static { 60 sCommands.put("exec", CommandId.EXEC); 61 sCommands.put("addMO", CommandId.ADD_MO); 62 sCommands.put("updateNode", CommandId.UPDATE_NODE); 63 sCommands.put("noMOUpdate", CommandId.NO_MO_UPDATE); 64 } 65 66 /** 67 * Execution types embedded in exec command for client to execute it. 68 */ 69 public class ExecCommandId { 70 public static final int BROWSER = 0; 71 public static final int GET_CERT = 1; 72 public static final int USE_CLIENT_CERT_TLS = 2; 73 public static final int UPLOAD_MO = 3; 74 } 75 76 private static final Map<String, Integer> sExecs = new HashMap<>(); 77 static { 78 sExecs.put("launchBrowserToURI", ExecCommandId.BROWSER); 79 sExecs.put("getCertificate", ExecCommandId.GET_CERT); 80 sExecs.put("useClientCertTLS", ExecCommandId.USE_CLIENT_CERT_TLS); 81 sExecs.put("uploadMO", ExecCommandId.UPLOAD_MO); 82 } 83 SppCommand(PropertyInfo soapResponse)84 private SppCommand(PropertyInfo soapResponse) throws IllegalArgumentException { 85 if (!sCommands.containsKey(soapResponse.getName())) { 86 throw new IllegalArgumentException("can't find the command: " + soapResponse.getName()); 87 } 88 mSppCommandId = sCommands.get(soapResponse.getName()); 89 90 Log.i(TAG, "command name: " + soapResponse.getName()); 91 92 switch(mSppCommandId) { 93 case CommandId.EXEC: 94 /* 95 * Receipt of this element by a mobile device causes the following command 96 * to be executed. 97 */ 98 SoapObject subCommand = (SoapObject) soapResponse.getValue(); 99 if (subCommand.getPropertyCount() != 1) { 100 throw new IllegalArgumentException( 101 "more than one child element found for exec command: " 102 + subCommand.getPropertyCount()); 103 } 104 105 PropertyInfo commandInfo = new PropertyInfo(); 106 subCommand.getPropertyInfo(0, commandInfo); 107 if (!sExecs.containsKey(commandInfo.getName())) { 108 throw new IllegalArgumentException( 109 "Unrecognized exec command: " + commandInfo.getName()); 110 } 111 mExecCommandId = sExecs.get(commandInfo.getName()); 112 Log.i(TAG, "exec command: " + commandInfo.getName()); 113 114 switch (mExecCommandId) { 115 case ExecCommandId.BROWSER: 116 /* 117 * When the mobile device receives this command, it launches its default 118 * browser to the URI contained in this element. The URI must use HTTPS as 119 * the protocol and must contain a FQDN. 120 */ 121 mCommandData = BrowserUri.createInstance(commandInfo); 122 break; 123 case ExecCommandId.GET_CERT: //fall-through 124 case ExecCommandId.UPLOAD_MO: //fall-through 125 case ExecCommandId.USE_CLIENT_CERT_TLS: //fall-through 126 /* 127 * Command to mobile to re-negotiate the TLS connection using a client 128 * certificate of the accepted type or Issuer to authenticate with the 129 * Subscription server. 130 */ 131 default: 132 mCommandData = null; 133 break; 134 } 135 break; 136 case CommandId.ADD_MO: 137 /* 138 * This command causes an management object in the mobile devices management tree 139 * at the specified location to be added. 140 * If there is already a management object at that location, the object is replaced. 141 */ 142 mCommandData = PpsMoData.createInstance(soapResponse); 143 break; 144 case CommandId.UPDATE_NODE: 145 /* 146 * This command causes the update of an interior node and its child nodes (if any) 147 * at the location specified in the management tree URI attribute. The content of 148 * this element is the MO node XML. 149 */ 150 break; 151 case CommandId.NO_MO_UPDATE: 152 /* 153 * This response is used when there is no command to be executed nor update of 154 * any MO required. 155 */ 156 break; 157 default: 158 mExecCommandId = -1; 159 mCommandData = null; 160 break; 161 } 162 } 163 164 /** 165 * Create an instance of {@link SppCommand} 166 * 167 * @param soapResponse SOAP Response received from server. 168 * @return instance of {@link SppCommand} 169 */ createInstance(@onNull PropertyInfo soapResponse)170 public static SppCommand createInstance(@NonNull PropertyInfo soapResponse) { 171 SppCommand sppCommand; 172 try { 173 sppCommand = new SppCommand(soapResponse); 174 } catch (IllegalArgumentException e) { 175 Log.e(TAG, "fails to create an instance: " + e); 176 return null; 177 } 178 return sppCommand; 179 } 180 getSppCommandId()181 public int getSppCommandId() { 182 return mSppCommandId; 183 } 184 getExecCommandId()185 public int getExecCommandId() { 186 return mExecCommandId; 187 } 188 getCommandData()189 public SppCommandData getCommandData() { 190 return mCommandData; 191 } 192 193 @Override equals(Object thatObject)194 public boolean equals(Object thatObject) { 195 if (this == thatObject) return true; 196 if (!(thatObject instanceof SppCommand)) return false; 197 SppCommand that = (SppCommand) thatObject; 198 return (mSppCommandId == that.getSppCommandId()) 199 && (mExecCommandId == that.getExecCommandId()) 200 && Objects.equals(mCommandData, that.getCommandData()); 201 } 202 203 @Override hashCode()204 public int hashCode() { 205 return Objects.hash(mSppCommandId, mExecCommandId, mCommandData); 206 } 207 208 @Override toString()209 public String toString() { 210 return "SppCommand{" 211 + "mSppCommandId=" + mSppCommandId 212 + ", mExecCommandId=" + mExecCommandId 213 + ", mCommandData=" + mCommandData 214 + "}"; 215 } 216 } 217