1 /*
2  * Copyright (C) 2023 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.cts.appcloning.contacts;
18 
19 import java.util.List;
20 
21 public class ContactsShellCommandHelper {
22 
23     private static final String CONTACTS_AUTHORITY_ENDPOINT = "content://com.android.contacts/";
24     private static final String CALLER_IS_SYNCADAPTER = "caller_is_syncadapter";
25     private static final String URI_TAG = "--uri";
26     private static final String BIND_TAG = "--bind";
27     private static final String USER_TAG = "--user";
28     private static final String WHERE_CLAUSE_TAG = "--where";
29 
30     private enum ContentOperationType {
31         INSERT("insert"),
32         DELETE("delete"),
33         QUERY("query"),
34         UPDATE("update");
35 
36         public final String operation;
ContentOperationType(String s)37         ContentOperationType(String s) {
38             this.operation = s;
39         }
40     }
41 
42     public static class ColumnBindings {
43         public final String columnName;
44         public final String columnValue;
45         public final Type columnType;
46 
47         enum Type {
48             STRING("s"),
49             INT("i"),
50             FLOAT("f"),
51             LONG("l"),
52             DOUBLE("d");
53 
54             public final String charValue;
55 
Type(String s)56             Type(String s) {
57                 this.charValue = s;
58             }
59         }
60 
ColumnBindings(String name, String value, Type type)61         public ColumnBindings(String name, String value, Type type) {
62             this.columnType = type;
63             this.columnName = name;
64             this.columnValue = value;
65         }
66     }
67 
68     /**
69      * Helper method that builds shell command for the content insert operation corresponding to the
70      * input values
71      * @param endpoint Contacts endpoint that the uri would access
72      * @param bindings Content values and the column name to which they should be bound
73      * @param user user identifier to redirect to the correct content provider
74      * @return String corresponding to the content insert shell command
75      */
getInsertTestContactCommand(String endpoint, List<ColumnBindings> bindings, String user)76     public static String getInsertTestContactCommand(String endpoint, List<ColumnBindings> bindings,
77             String user) {
78         return buildContentOperationCommand(ContentOperationType.INSERT.operation, endpoint,
79                 /* callerIsSyncAdapter */false, bindings, /* whereOperation */ null, user);
80     }
81 
82     /**
83      * Helper method that builds shell command for the content delete operation corresponding to the
84      * input values
85      * @param endpoint Contacts endpoint that the uri would access
86      * @param whereClause String that specifies the where clause to identify the rows that the
87      *                    query will be run on
88      * @param user user identifier to redirect to the correct content provider
89      * @return String corresponding to the content delete shell command
90      */
getDeleteContactCommand(String endpoint, String whereClause, String user)91     public static String getDeleteContactCommand(String endpoint, String whereClause, String user) {
92         return buildContentOperationCommand(ContentOperationType.DELETE.operation, endpoint,
93                 /* callerIsSyncAdapter */ true, /* bindValues */ null, whereClause, user);
94     }
95 
96     /**
97      *
98      * @param endpoint Contacts endpoint that the uri would access
99      * @param whereClause String that specifies the where clause to identify the rows that the
100      *                    query will be run on
101      * @param user user identifier to redirect to the correct content provider
102      * @return String corresponding to the content query shell command
103      * @return
104      */
getQueryTestContactsCommand(String endpoint, String whereClause, String user)105     public static String getQueryTestContactsCommand(String endpoint, String whereClause,
106             String user) {
107         return buildContentOperationCommand(ContentOperationType.QUERY.operation, endpoint,
108                 /* callerIsSyncAdapter */ false, /* bindValues */ null, whereClause, user);
109     }
110 
111     /**
112      * A helper method that builds the shell command for a content operation
113      * @param operationType content operation type - insert/delete/query/update
114      * @param endpoint Contacts endpoint that the uri would access
115      * @param callerIsSyncAdapter boolean value indicating if caller is a sync adapter
116      * @param bindValues Content values and the column name to which they should be bound
117      * @param whereOperations String that specifies the where clause to identify the rows that the
118      *                        query will be run on
119      * @param user user identifier to redirect to the correct content provider
120      */
buildContentOperationCommand(String operationType, String endpoint, boolean callerIsSyncAdapter, List<ColumnBindings> bindValues, String whereOperations, String user)121     private static String buildContentOperationCommand(String operationType,
122             String endpoint, boolean callerIsSyncAdapter,
123             List<ColumnBindings> bindValues, String whereOperations,
124             String user) {
125         if (operationType == null || endpoint == null) {
126             return null;
127         }
128 
129         String contentOperation = "content " + operationType;
130 
131         // Build the uri part of the command
132         StringBuilder uriPart = new StringBuilder();
133         uriPart.append(URI_TAG).append(" ")
134                 .append(CONTACTS_AUTHORITY_ENDPOINT)
135                 .append(endpoint);
136         if (callerIsSyncAdapter) {
137             uriPart.append("?")
138                     .append(CALLER_IS_SYNCADAPTER)
139                     .append("=true");
140         }
141 
142         // Build the column to value bindings part of the command
143         StringBuilder bindingsString = new StringBuilder();
144         if (bindValues != null && !bindValues.isEmpty()) {
145             for (ColumnBindings binding: bindValues) {
146                 bindingsString.append(BIND_TAG).append(" ")
147                         .append(binding.columnName)
148                         .append(":")
149                         .append(binding.columnType.charValue)
150                         .append(":")
151                         .append(binding.columnValue)
152                         .append(" ");
153             }
154         }
155 
156         // Build the where clause of the command
157         StringBuilder where = new StringBuilder();
158         if (whereOperations != null) {
159             where.append(WHERE_CLAUSE_TAG).append(" ")
160                     .append(whereOperations);
161         }
162 
163         // Build user part of the command if user id is provided
164         StringBuilder userPart = new StringBuilder();
165         if (user != null) {
166             userPart.append(USER_TAG).append(" ")
167                     .append(user);
168         }
169         return contentOperation + " " + uriPart + " " + bindingsString + " " + where + " "
170                 + userPart;
171     }
172 }
173