1 /* 2 * Copyright (C) 2013 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.tradefed.command.remote; 18 19 import org.json.JSONException; 20 import org.json.JSONObject; 21 22 /** 23 * Encapsulates data for a remote operation sent over the wire. 24 */ 25 abstract class RemoteOperation<T> { 26 private static final String TYPE = "type"; 27 private static final String VERSION = "version"; 28 /** represents json key for error message */ 29 static final String ERROR = "error"; 30 31 static final int CURRENT_PROTOCOL_VERSION = 8; 32 33 /** 34 * Represents all types of remote operations that can be performed 35 */ 36 enum OperationType { 37 ALLOCATE_DEVICE, 38 FREE_DEVICE, 39 CLOSE, 40 ADD_COMMAND, 41 START_HANDOVER, 42 LIST_DEVICES, 43 EXEC_COMMAND, 44 GET_LAST_COMMAND_RESULT, 45 HANDOVER_COMPLETE, 46 ADD_COMMAND_FILE, 47 HANDOVER_INIT_COMPLETE, 48 } 49 50 /** 51 * Create and populate a {@link RemoteOperation} from given data. 52 * 53 * @param data the data to parse 54 * @throws RemoteException 55 */ 56 final static RemoteOperation<?> createRemoteOpFromString(String data) throws RemoteException { 57 try { 58 JSONObject jsonData = new JSONObject(data); 59 int protocolVersion = jsonData.getInt(VERSION); 60 // to keep things simple for now, just barf when protocol version is unknown 61 if (protocolVersion != CURRENT_PROTOCOL_VERSION) { 62 throw new RemoteException(String.format( 63 "Remote operation has unknown version '%d'. Expected '%d'", 64 protocolVersion, CURRENT_PROTOCOL_VERSION)); 65 } 66 OperationType op = OperationType.valueOf(jsonData.getString(TYPE)); 67 RemoteOperation<?> rc = null; 68 switch (op) { 69 case ALLOCATE_DEVICE: 70 rc = AllocateDeviceOp.createFromJson(jsonData); 71 break; 72 case FREE_DEVICE: 73 rc = FreeDeviceOp.createFromJson(jsonData); 74 break; 75 case CLOSE: 76 rc = CloseOp.createFromJson(jsonData); 77 break; 78 case ADD_COMMAND: 79 rc = AddCommandOp.createFromJson(jsonData); 80 break; 81 case START_HANDOVER: 82 rc = StartHandoverOp.createFromJson(jsonData); 83 break; 84 case LIST_DEVICES: 85 rc = ListDevicesOp.createFromJson(jsonData); 86 break; 87 case EXEC_COMMAND: 88 rc = ExecCommandOp.createFromJson(jsonData); 89 break; 90 case GET_LAST_COMMAND_RESULT: 91 rc = GetLastCommandResultOp.createFromJson(jsonData); 92 break; 93 case HANDOVER_INIT_COMPLETE: 94 rc = HandoverInitCompleteOp.createFromJson(jsonData); 95 break; 96 case HANDOVER_COMPLETE: 97 rc = HandoverCompleteOp.createFromJson(jsonData); 98 break; 99 case ADD_COMMAND_FILE: 100 rc = AddCommandFileOp.createFromJson(jsonData); 101 break; 102 default: 103 throw new RemoteException(String.format("unknown remote command '%s'", data)); 104 105 } 106 return rc; 107 } catch (JSONException e) { 108 throw new RemoteException(e); 109 } 110 } 111 112 protected abstract OperationType getType(); 113 114 /** 115 * Returns the RemoteCommand data in its wire protocol format 116 */ 117 String pack() throws RemoteException { 118 return pack(CURRENT_PROTOCOL_VERSION); 119 } 120 121 /** 122 * Returns the RemoteCommand data in its wire protocol format, with given protocol version 123 * 124 * @VisibleForTesting 125 */ 126 String pack(int protocolVersion) throws RemoteException { 127 JSONObject j = new JSONObject(); 128 try { 129 j.put(VERSION, protocolVersion); 130 j.put(TYPE, getType().toString()); 131 packIntoJson(j); 132 } catch (JSONException e) { 133 throw new RemoteException("Failed to serialize RemoteOperation", e); 134 } 135 return j.toString(); 136 } 137 138 /** 139 * Callback to add subclass specific data to the JSON object 140 * 141 * @param j 142 * @throws JSONException 143 */ 144 protected abstract void packIntoJson(JSONObject j) throws JSONException; 145 146 /** 147 * Optional callback to parse additional response data from the JSON object 148 * 149 * @param j 150 * @throws JSONException 151 */ 152 protected T unpackResponseFromJson(JSONObject j) throws JSONException { 153 return null; 154 } 155 156 /** 157 * Parse out the remote op response data from string 158 * 159 * @param response 160 * @throws JSONException 161 */ 162 T unpackResponseFromString(String response) throws JSONException, RemoteException { 163 JSONObject jsonData = new JSONObject(response); 164 if (jsonData.has(ERROR)) { 165 throw new RemoteException(jsonData.getString(ERROR)); 166 } 167 return unpackResponseFromJson(jsonData); 168 } 169 } 170