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      */
createRemoteOpFromString(String data)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 
getType()112     protected abstract OperationType getType();
113 
114     /**
115      * Returns the RemoteCommand data in its wire protocol format
116      */
pack()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      */
pack(int protocolVersion)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      */
packIntoJson(JSONObject j)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      */
unpackResponseFromJson(JSONObject j)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      */
unpackResponseFromString(String response)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