├── .gitallowed
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── notebooks
├── codelab_kiosk.ipynb
├── managed_configurations.ipynb
├── quickstart.ipynb
└── web_apps.ipynb
└── www
└── oauth_callback.html
/.gitallowed:
--------------------------------------------------------------------------------
1 | # Client ID and secret used for the sample notebook
2 | 882252295571-uvkkfelq073vq73bbq9cmr0rn8bt80ee.apps.googleusercontent.com
3 | S2QcoBe0jxNLUoqnpeksCLxI
4 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to Contribute
2 |
3 | We'd love to accept your patches and contributions to this project. There are
4 | just a few small guidelines you need to follow.
5 |
6 | ## Contributor License Agreement
7 |
8 | Contributions to this project must be accompanied by a Contributor License
9 | Agreement. You (or your employer) retain the copyright to your contribution;
10 | this simply gives us permission to use and redistribute your contributions as
11 | part of the project. Head over to to see
12 | your current agreements on file or to sign a new one.
13 |
14 | You generally only need to submit a CLA once, so if you've already submitted one
15 | (even if it was for a different project), you probably don't need to do it
16 | again.
17 |
18 | ## Code reviews
19 |
20 | All submissions, including submissions by project members, require review. We
21 | use GitHub pull requests for this purpose. Consult
22 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
23 | information on using pull requests.
24 |
25 | ## Community Guidelines
26 |
27 | This project follows [Google's Open Source Community
28 | Guidelines](https://opensource.google.com/conduct/).
29 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright [yyyy] [name of copyright owner]
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Android Management API Samples
2 |
3 | This repository contains Python notebooks for getting started with the
4 | [Android Management API](https://developers.google.com/android/management).
5 |
6 | You can run the notebooks in [Google Colab](https://colab.research.google.com)
7 | using the following links:
8 |
9 | * [quickstart.ipynb](https://colab.research.google.com/github/google/android-management-api-samples/blob/master/notebooks/quickstart.ipynb)
10 | * [web_apps.ipynb](https://colab.research.google.com/github/google/android-management-api-samples/blob/master/notebooks/web_apps.ipynb)
11 | * [managed_configurations.ipynb](https://colab.research.google.com/github/google/android-management-api-samples/blob/master/notebooks/managed_configurations.ipynb)
12 |
--------------------------------------------------------------------------------
/notebooks/codelab_kiosk.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {
6 | "id": "lbI8y3v1G6OM"
7 | },
8 | "source": [
9 | "#### Copyright 2022 Google LLC.\n",
10 | "\n",
11 | "Licensed under the Apache License, Version 2.0 (the \"License\");"
12 | ]
13 | },
14 | {
15 | "cell_type": "code",
16 | "execution_count": null,
17 | "metadata": {
18 | "id": "JxL-8R6KHRtK"
19 | },
20 | "outputs": [],
21 | "source": [
22 | "# Licensed under the Apache License, Version 2.0 (the \"License\");\n",
23 | "# you may not use this file except in compliance with the License.\n",
24 | "# You may obtain a copy of the License at\n",
25 | "\n",
26 | "# https://www.apache.org/licenses/LICENSE-2.0\n",
27 | "\n",
28 | "# Unless required by applicable law or agreed to in writing, software\n",
29 | "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
30 | "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
31 | "# See the License for the specific language governing permissions and\n",
32 | "# limitations under the License."
33 | ]
34 | },
35 | {
36 | "cell_type": "markdown",
37 | "metadata": {
38 | "id": "3Sf3qQL9CrIG"
39 | },
40 | "source": [
41 | "# Android Management API - Quickstart\n",
42 | "\n",
43 | "If you have not yet read the Android Management API Codelab we recommend that you do so before using this notebook. If you opened this notebook from the Codelab then follow the next instructions on the Codelab.\n",
44 | "\n",
45 | "In order to run this notebook, you need:\n",
46 | "\n",
47 | "* An Android 6.0+ device.\n",
48 | "\n",
49 | "## Setup\n",
50 | "\n",
51 | "The base resource of your Android Management solution is a Google Cloud Platform project. All other resources (`Enterprises`, `Devices`, `Policies`, etc) belong to the project and the project controls access to these resources. A solution is typically associated with a single project, but you can create multiple projects if you want to restrict access to resources.\n",
52 | "\n",
53 | "For this Codelab we have already created a project for you (project ID: `android-management-io-codelab`).\n",
54 | "\n",
55 | "To create and access resources, you need to authenticate with an account that has edit rights over the project. The account running this Codelab has been given rights over the project above. To start the authentication flow, **run the cell below**.\n",
56 | "\n",
57 | "To run a cell:\n",
58 | "\n",
59 | "1. Click anywhere in the code block.\n",
60 | "2. Click the \u0026#9654; button in the top-left of the code block.\n",
61 | "\n",
62 | "When you build a server-based solution, you should create a\n",
63 | "[service account](https://developers.google.com/android/management/service-account)\n",
64 | "so you don't need to authorize the access every time."
65 | ]
66 | },
67 | {
68 | "cell_type": "code",
69 | "execution_count": null,
70 | "metadata": {
71 | "id": "8myK1D6uy7HR"
72 | },
73 | "outputs": [],
74 | "source": [
75 | "from apiclient.discovery import build\n",
76 | "from google_auth_oauthlib.flow import Flow\n",
77 | "from random import randint\n",
78 | "\n",
79 | "# This is a public OAuth config that you can use to run this guide.\n",
80 | "# However, use different credentials when building your own solution.\n",
81 | "CLIENT_CONFIG = {\n",
82 | " 'web': {\n",
83 | " 'client_id':'882252295571-m8qpit609pj2hsql4j5k0bue1injrtd2.apps.googleusercontent.com',\n",
84 | " 'auth_uri':'https://accounts.google.com/o/oauth2/auth',\n",
85 | " 'token_uri':'https://oauth2.googleapis.com/token',\n",
86 | " 'auth_provider_x509_cert_url':'https://www.googleapis.com/oauth2/v1/certs',\n",
87 | " 'client_secret':'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'\n",
88 | " }\n",
89 | "}\n",
90 | "SCOPES = ['https://www.googleapis.com/auth/androidmanagement']\n",
91 | "CALLBACK_URL = 'https://google.github.io/android-management-api-samples/oauth_callback.html'\n",
92 | "\n",
93 | "# Run the OAuth flow.\n",
94 | "flow = Flow.from_client_config(CLIENT_CONFIG, SCOPES)\n",
95 | "flow.redirect_uri = CALLBACK_URL\n",
96 | "auth_url, _ = flow.authorization_url()\n",
97 | "print('Please visit this URL to authorize this application: {}'.format(auth_url))\n",
98 | "\n",
99 | "code = input('Enter the authorization code: ')\n",
100 | "flow.fetch_token(code=code)\n",
101 | "\n",
102 | "# Create the API client.\n",
103 | "androidmanagement = build('androidmanagement', 'v1', credentials=flow.credentials)\n",
104 | "\n",
105 | "print('\\nAuthentication succeeded.')"
106 | ]
107 | },
108 | {
109 | "cell_type": "markdown",
110 | "metadata": {
111 | "id": "bsnuDBT7JQ9O"
112 | },
113 | "source": [
114 | "## Select an enterprise\n",
115 | "\n",
116 | "An **`Enterprise`** resource binds an organization to your Android Management solution.\n",
117 | "**`Devices`** and **`Policies`** both belong to an enterprise. Typically, a single enterprise\n",
118 | "resource is associated with a single organization. However, you can create multiple\n",
119 | "enterprises for the same organization based on their needs. For example, an\n",
120 | "organization may want separate enterprises for its different departments or regions.\n",
121 | "\n",
122 | "For this Codelab we have already created an enterprise for you. **Run the next cell** to select it."
123 | ]
124 | },
125 | {
126 | "cell_type": "code",
127 | "execution_count": null,
128 | "metadata": {
129 | "id": "R4rNq_O_BNBJ"
130 | },
131 | "outputs": [],
132 | "source": [
133 | "enterprise_name = 'enterprises/LC02de1hmx'"
134 | ]
135 | },
136 | {
137 | "cell_type": "markdown",
138 | "metadata": {
139 | "id": "urkbkf7zJgqN"
140 | },
141 | "source": [
142 | "## Create a policy\n",
143 | "\n",
144 | "A **`Policy`** is a group of settings that determine the behavior of a managed device\n",
145 | "and the apps installed on it. Each Policy resource represents a unique group of device\n",
146 | "and app settings and can be applied to one or more devices. Once a device is linked to\n",
147 | "a policy, any updates to the policy are automatically applied to the device.\n",
148 | "\n",
149 | "To create a basic policy, **run the cell below**. You'll see how to create more advanced policies later in this guide."
150 | ]
151 | },
152 | {
153 | "cell_type": "code",
154 | "execution_count": null,
155 | "metadata": {
156 | "id": "pjHfDSb8BoBP"
157 | },
158 | "outputs": [],
159 | "source": [
160 | "import json\n",
161 | "\n",
162 | "# Create a random policy name to avoid colision with other Codelabs\n",
163 | "if 'policy_name' not in locals():\n",
164 | " policy_name = enterprise_name + '/policies/' + str(randint(1, 1000000000))\n",
165 | "\n",
166 | "policy_json = '''\n",
167 | "{\n",
168 | " \"applications\": [\n",
169 | " {\n",
170 | " \"packageName\": \"com.google.samples.apps.iosched\",\n",
171 | " \"installType\": \"FORCE_INSTALLED\"\n",
172 | " }\n",
173 | " ],\n",
174 | " \"advancedSecurityOverrides\": {\n",
175 | " \"developerSettings\": \"DEVELOPER_SETTINGS_ALLOWED\"\n",
176 | " }\n",
177 | "}\n",
178 | "'''\n",
179 | "\n",
180 | "androidmanagement.enterprises().policies().patch(\n",
181 | " name=policy_name,\n",
182 | " body=json.loads(policy_json)\n",
183 | ").execute()"
184 | ]
185 | },
186 | {
187 | "cell_type": "markdown",
188 | "metadata": {
189 | "id": "SyxrXcIMJuDF"
190 | },
191 | "source": [
192 | "## Provision a device\n",
193 | "\n",
194 | "Provisioning refers to the process of enrolling a device with an enterprise, applying the appropriate policies to the device, and guiding the user to complete the set up of their device in accordance with those policies. Before attempting to provision a device, ensure that the device is running Android 6.0 or above.\n",
195 | "\n",
196 | "You need an enrollment token for each device that you want to provision (you can use the same token for multiple devices), when creating a token you can specify a policy that will be applied to the device."
197 | ]
198 | },
199 | {
200 | "cell_type": "code",
201 | "execution_count": null,
202 | "metadata": {
203 | "id": "JPYgBZSmCEil"
204 | },
205 | "outputs": [],
206 | "source": [
207 | "enrollment_token = androidmanagement.enterprises().enrollmentTokens().create(\n",
208 | " parent=enterprise_name,\n",
209 | " body={\"policyName\": policy_name}\n",
210 | ").execute()"
211 | ]
212 | },
213 | {
214 | "cell_type": "markdown",
215 | "metadata": {
216 | "id": "zmq6yvGsJ3pJ"
217 | },
218 | "source": [
219 | "Embed your enrollment token in either an enrollment link or a QR code, and then follow the provisioning instructions below."
220 | ]
221 | },
222 | {
223 | "cell_type": "code",
224 | "execution_count": null,
225 | "metadata": {
226 | "id": "TTSgnpC9EScx"
227 | },
228 | "outputs": [],
229 | "source": [
230 | "from IPython.display import display, HTML\n",
231 | "\n",
232 | "qr_code_content = enrollment_token['qrCode']\n",
233 | "qr_code_settings = {\n",
234 | " 'text': qr_code_content,\n",
235 | " 'width': 500,\n",
236 | " 'height': 500\n",
237 | "}\n",
238 | "\n",
239 | "display(HTML('''\n",
240 | " \u003cdiv id=\"qr_code\" style=\"padding: 16px; background: #FFF; width: 500px;\"\u003e\u003c/div\u003e\n",
241 | " \u003cscript type=\"text/javascript\" src=\"//cdn.rawgit.com/davidshimjs/qrcodejs/gh-pages/qrcode.min.js\"\u003e\u003c/script\u003e\n",
242 | " \u003cscript type=\"text/javascript\"\u003e\n",
243 | " new QRCode(document.getElementById(\"qr_code\"), {});\n",
244 | " \u003c/script\u003e\n",
245 | "'''.format(qr_code_settings)))"
246 | ]
247 | },
248 | {
249 | "cell_type": "code",
250 | "execution_count": null,
251 | "metadata": {
252 | "id": "7ICRh_3UsiU4"
253 | },
254 | "outputs": [],
255 | "source": [
256 | "enrollment_link = 'https://enterprise.google.com/android/enroll?et=' + enrollment_token['value']\n",
257 | "\n",
258 | "print('Please open this link on your device:', enrollment_link)"
259 | ]
260 | },
261 | {
262 | "cell_type": "markdown",
263 | "metadata": {
264 | "id": "hcdeKcrIfM5H"
265 | },
266 | "source": [
267 | "The method for provisioning a device varies depending on the management mode you want to use.\n",
268 | "\n",
269 | "### Fully managed mode\n",
270 | "\n",
271 | "In **fully managed mode** the entire device is managed and the device needs to be factory reset before setup. To set up a device in fully managed mode you need to use a QR code.\n",
272 | "\n",
273 | "For devices running Android 7.0 or above:\n",
274 | "\n",
275 | "1. Turn on a new or factory-reset device.\n",
276 | "2. Tap the same spot on the welcome screen six times to enter QR code mode.\n",
277 | "3. Connect to a WiFi network.\n",
278 | "4. Scan the QR code.\n",
279 | "\n",
280 | "For devices running Android 6.0:\n",
281 | "\n",
282 | "1. Turn on a new or factory-reset device.\n",
283 | "2. Follow the setup wizard and enter your Wi-Fi details.\n",
284 | "3. When prompted to sign in, enter **afw#setup**.\n",
285 | "4. Tap Next, and then accept the installation of Android Device Policy.\n",
286 | "5. Scan the QR code.\n",
287 | "\n",
288 | "### Work profile mode\n",
289 | "\n",
290 | "In **work profile mode** corporate apps and data are kept secure in a self-contained work profile while the user keeps control of the rest of the device. To set up a work profile you can either use a QR code or an enrollment link.\n",
291 | "\n",
292 | "Using the enrollment link:\n",
293 | "\n",
294 | "1. Make the link accessible on the device (send it via email or put it on a website).\n",
295 | "2. Open the link.\n",
296 | "\n",
297 | "Or using the QR code:\n",
298 | "\n",
299 | "1. Go to **Settings** \u003e **Google**.\n",
300 | "2. Tap \"Set up your work profile\".\n",
301 | "3. Scan the QR code.\n",
302 | "\n",
303 | "## What's next?\n",
304 | "\n",
305 | "By now you should have a managed device configured with a basic policy, but there's\n",
306 | "much more you can do with the Android Management API.\n",
307 | "\n",
308 | "First, we recommend\n",
309 | "[**exploring the range of available policies**](https://developers.google.com/android/management/create-policy)\n",
310 | "to build the right policy for your needs. \n",
311 | "\n",
312 | "Next, explore other features of the Android Management API:\n",
313 | "\n",
314 | "* Learn how to [discover apps](https://developers.google.com/android/management/apps)\n",
315 | "* Set up [Pub/Sub notifications](https://developers.google.com/android/management/notifications)\n",
316 | "\n",
317 | "Or start developing a server-based solution:\n",
318 | "\n",
319 | "* Download the Android Management API client library for\n",
320 | " [Java](https://developers.google.com/api-client-library/java/apis/androidmanagement/v1),\n",
321 | " [.NET](https://developers.google.com/api-client-library/dotnet/apis/androidmanagement/v1),\n",
322 | " [Python](https://developers.google.com/api-client-library/python/apis/androidmanagement/v1),\n",
323 | " or [Ruby](https://developers.google.com/api-client-library/ruby/apis/androidmanagement/v1).\n",
324 | "* Create a [service account](https://developers.google.com/android/management/service-account).\n",
325 | "\n"
326 | ]
327 | }
328 | ],
329 | "metadata": {
330 | "colab": {
331 | "collapsed_sections": [
332 | "lbI8y3v1G6OM"
333 | ],
334 | "name": "Android Management API - Quickstart (Codelab kiosk)",
335 | "private_outputs": true,
336 | "provenance": [],
337 | "toc_visible": true
338 | },
339 | "kernelspec": {
340 | "display_name": "Python 3",
341 | "name": "python3"
342 | }
343 | },
344 | "nbformat": 4,
345 | "nbformat_minor": 0
346 | }
347 |
--------------------------------------------------------------------------------
/notebooks/managed_configurations.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {
6 | "id": "lbI8y3v1G6OM"
7 | },
8 | "source": [
9 | "#### Copyright 2022 Google LLC.\n",
10 | "\n",
11 | "Licensed under the Apache License, Version 2.0 (the \"License\");"
12 | ]
13 | },
14 | {
15 | "cell_type": "code",
16 | "execution_count": null,
17 | "metadata": {
18 | "id": "JxL-8R6KHRtK"
19 | },
20 | "outputs": [],
21 | "source": [
22 | "# Licensed under the Apache License, Version 2.0 (the \"License\");\n",
23 | "# you may not use this file except in compliance with the License.\n",
24 | "# You may obtain a copy of the License at\n",
25 | "\n",
26 | "# https://www.apache.org/licenses/LICENSE-2.0\n",
27 | "\n",
28 | "# Unless required by applicable law or agreed to in writing, software\n",
29 | "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
30 | "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
31 | "# See the License for the specific language governing permissions and\n",
32 | "# limitations under the License."
33 | ]
34 | },
35 | {
36 | "cell_type": "markdown",
37 | "metadata": {
38 | "id": "8CY7hBo9gq04"
39 | },
40 | "source": [
41 | "# How to set managed configurations for an app and retrieve app feedback\n",
42 | "\n",
43 | "This notebook shows you how to support the [managed configurations](https://developers.google.com/android/management/managed-configurations-iframe) and how to [retrieve feedback from apps](https://developers.google.com/android/management/app-feedback) with the Android Management API. If an app supports managed configurations, sending keyed app states is recommended as a way to update IT admins on the status of the configurations they set. \n",
44 | "\n",
45 | "To check if an app supports managed configurations:\n",
46 | "\n",
47 | "1. Go to [managed Google Play](https://play.google.com/work).\n",
48 | "2. Search for, and click the app you’d like to check.\n",
49 | "3. If the production version of the app supports managed configuration, the following message is shown under the Approve or Buy button: This app offers managed configuration.\n",
50 | "\n",
51 | "## Setup\n",
52 | "\n",
53 | "You can use the [Android Management API quickstart guide](https://developers.google.com/android/management/quickstart) to become familiar with how to use this colab notebook. The quickstart will help you create a cloud project, enroll an enterprise, and provision a device.\n",
54 | "\n",
55 | "In this colab you will need to have:\n",
56 | "\n",
57 | "* Enrolled an enterprise.\n",
58 | "* Created a policy.\n",
59 | "* Provisioned an Android 6.0+ device under your enterprise.\n",
60 | "\n",
61 | "You will use the resources (`Enterprises`, `Devices`, `Policies`) that you have created from the quickstart.\n"
62 | ]
63 | },
64 | {
65 | "cell_type": "markdown",
66 | "metadata": {
67 | "id": "B-Xfg4D4Xqnu"
68 | },
69 | "source": [
70 | "# Set up the managed configurations iframe\n",
71 | "\n",
72 | "\n",
73 | "## Authenticating\n",
74 | "To create and access resources, you need to authenticate with an account that has edit rights over your project. To start the authentication flow, **run the cell below**.\n",
75 | "\n",
76 | "When you build a server-based solution, you should create a\n",
77 | "[service account](https://developers.google.com/android/management/service-account)\n",
78 | "so you don't need to authorize the access every time."
79 | ]
80 | },
81 | {
82 | "cell_type": "code",
83 | "execution_count": null,
84 | "metadata": {
85 | "id": "x5hRUqoeXqnx"
86 | },
87 | "outputs": [],
88 | "source": [
89 | "import json\n",
90 | "from apiclient.discovery import build\n",
91 | "from google_auth_oauthlib.flow import Flow\n",
92 | "import pprint\n",
93 | "\n",
94 | "# To improve the readability of deeply nested data structures, we create a \n",
95 | "# helper function to pretty print a result:\n",
96 | "prettyprint = pprint.PrettyPrinter(indent=4).pprint\n",
97 | "\n",
98 | "# This is a public OAuth config that you can use to run this guide.\n",
99 | "# However, use different credentials when building your own solution.\n",
100 | "CLIENT_CONFIG = {\n",
101 | " 'web': {\n",
102 | " 'client_id':'882252295571-m8qpit609pj2hsql4j5k0bue1injrtd2.apps.googleusercontent.com',\n",
103 | " 'auth_uri':'https://accounts.google.com/o/oauth2/auth',\n",
104 | " 'token_uri':'https://oauth2.googleapis.com/token',\n",
105 | " 'auth_provider_x509_cert_url':'https://www.googleapis.com/oauth2/v1/certs',\n",
106 | " 'client_secret':'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'\n",
107 | " }\n",
108 | "}\n",
109 | "SCOPES = ['https://www.googleapis.com/auth/androidmanagement']\n",
110 | "CALLBACK_URL = 'https://google.github.io/android-management-api-samples/oauth_callback.html'\n",
111 | "\n",
112 | "# Run the OAuth flow.\n",
113 | "flow = Flow.from_client_config(CLIENT_CONFIG, SCOPES)\n",
114 | "flow.redirect_uri = CALLBACK_URL\n",
115 | "auth_url, _ = flow.authorization_url()\n",
116 | "print('Please visit this URL to authorize this application: {}'.format(auth_url))\n",
117 | "\n",
118 | "code = input('Enter the authorization code: ')\n",
119 | "flow.fetch_token(code=code)\n",
120 | "\n",
121 | "# Create the API client.\n",
122 | "androidmanagement = build('androidmanagement', 'v1', credentials=flow.credentials)\n",
123 | "\n",
124 | "print('\\nAuthentication succeeded.')"
125 | ]
126 | },
127 | {
128 | "cell_type": "markdown",
129 | "metadata": {
130 | "id": "8Mt-79teXqn0"
131 | },
132 | "source": [
133 | "## Declare your enterprise\n",
134 | "\n",
135 | "If you already have an enterprise, you can enter the enterprise name in the cell below and run the cell. If you do not have a enterprise, please go through the [general Quickstart](https://developers.google.com/android/management/quickstart)"
136 | ]
137 | },
138 | {
139 | "cell_type": "code",
140 | "execution_count": null,
141 | "metadata": {
142 | "id": "R4rNq_O_BNBJ"
143 | },
144 | "outputs": [],
145 | "source": [
146 | "# Paste your enterprise name here.\n",
147 | "# It should be of the format 'enterprises/LC...'\n",
148 | "enterprise_name = ''"
149 | ]
150 | },
151 | {
152 | "cell_type": "markdown",
153 | "metadata": {
154 | "id": "VRV6mzs5ou8w"
155 | },
156 | "source": [
157 | "## Select an application to configure\n",
158 | "\n",
159 | "You can check in [Google Play](https://play.google.com/work) if an application supports managed configuration."
160 | ]
161 | },
162 | {
163 | "cell_type": "code",
164 | "execution_count": null,
165 | "metadata": {
166 | "id": "NA31sCiUoiNZ"
167 | },
168 | "outputs": [],
169 | "source": [
170 | "# Paste the package name of an application here.\n",
171 | "package_name = ''"
172 | ]
173 | },
174 | {
175 | "cell_type": "markdown",
176 | "metadata": {
177 | "id": "w5X6UYB8Dqbx"
178 | },
179 | "source": [
180 | "# There are two ways you can support managed configurations in your EMM console:\n",
181 | "\n",
182 | "* Add the managed configurations iframe to your console and apply settings via managedConfigurationTemplate in ApplicationPolicy\n",
183 | "* Create your own UI and apply the settings via managedConfiguration in ApplicationPolicy.\n",
184 | "\n",
185 | "Managed configurations allow the organization's IT admin to remotely specify settings for apps. To view sample apps with a managed configuration, see [AppRestrictionSchema](https://github.com/android/enterprise-samples/tree/master/AppRestrictionSchema/#readme)."
186 | ]
187 | },
188 | {
189 | "cell_type": "markdown",
190 | "metadata": {
191 | "id": "W6H-nD3uT6no"
192 | },
193 | "source": [
194 | "## Option 1: Set managed configuration using the managed configuration iframe\n",
195 | "\n",
196 | "This section shows you how to support [managed configurations iframe](https://developers.google.com/android/management/managed-configurations-iframe) with the Android Management API. The managed configurations iframe is an embeddable UI that lets IT admins save, edit, and delete an app’s managed configuration settings."
197 | ]
198 | },
199 | {
200 | "cell_type": "markdown",
201 | "metadata": {
202 | "id": "MaWdKKR0rYWM"
203 | },
204 | "source": [
205 | "### Render the managed configuration iframe\n",
206 | "\n",
207 | "The iframe retrieves and displays the managed configurations schema for a specified app. Within the iframe, an IT admin can set configurations and save them as a configuration profile."
208 | ]
209 | },
210 | {
211 | "cell_type": "code",
212 | "execution_count": null,
213 | "metadata": {
214 | "id": "Q_1V5jbCUGWS"
215 | },
216 | "outputs": [],
217 | "source": [
218 | "IFRAME_URL = 'https://storage.googleapis.com/android-management-api-samples/managed_configuration_iframe.html'\n",
219 | "\n",
220 | "# Creates a web token to access an embeddable managed Google Play web UI for a given enterprise.\n",
221 | "web_token = androidmanagement.enterprises().webTokens().create(\n",
222 | " parent=enterprise_name,\n",
223 | " body={\n",
224 | " 'parentFrameUrl': IFRAME_URL\n",
225 | " }\n",
226 | ").execute()\n",
227 | "\n",
228 | "full_iframe_url = IFRAME_URL + \"?token=\" + web_token[\"value\"] + \"\u0026packageName=\" + package_name\n",
229 | "\n",
230 | "print('Open the managed configuration iframe:', full_iframe_url)"
231 | ]
232 | },
233 | {
234 | "cell_type": "code",
235 | "execution_count": null,
236 | "metadata": {
237 | "id": "yBNvr_0Fo-gv"
238 | },
239 | "outputs": [],
240 | "source": [
241 | "# Each time an IT admin saves a new configuration profile, the iframe returns a unique identifier called mcmId.\n",
242 | "# Paste the mcmId of the managed configurations profile you just created here.\n",
243 | "mdm_id = \"\""
244 | ]
245 | },
246 | {
247 | "cell_type": "markdown",
248 | "metadata": {
249 | "id": "7Uxjp4BLpdoF"
250 | },
251 | "source": [
252 | "### Set the managed configuration profile in the policy\n",
253 | "\n",
254 | "Policies (also called a policy) are the core resource of the Android Management API. You use them to create and save groups of device and app management settings for your customers to apply to devices. Each configuration profile is saved as a unique mcmId. To apply a configuration profile to a policy, specify mcmId in managedConfigurationTemplate."
255 | ]
256 | },
257 | {
258 | "cell_type": "code",
259 | "execution_count": null,
260 | "metadata": {
261 | "id": "3OSBO0Kfpc-e"
262 | },
263 | "outputs": [],
264 | "source": [
265 | "policy_name = enterprise_name + '/policies/managed-configurations'\n",
266 | "\n",
267 | "policy = {\n",
268 | " 'applications': [\n",
269 | " {\n",
270 | " 'installType': 'FORCE_INSTALLED',\n",
271 | " 'packageName': package_name,\n",
272 | " 'managedConfigurationTemplate': {\n",
273 | " 'templateId': mdm_id\n",
274 | " }\n",
275 | " }\n",
276 | " ],\n",
277 | " 'debuggingFeaturesAllowed': True\n",
278 | "}\n",
279 | "\n",
280 | "androidmanagement.enterprises().policies().patch(\n",
281 | " name=policy_name,\n",
282 | " body=policy\n",
283 | ").execute()"
284 | ]
285 | },
286 | {
287 | "cell_type": "markdown",
288 | "metadata": {
289 | "id": "rX1WRkE1qvBz"
290 | },
291 | "source": [
292 | "## Option 2: Set managed configurations through policy"
293 | ]
294 | },
295 | {
296 | "cell_type": "code",
297 | "execution_count": null,
298 | "metadata": {
299 | "id": "TkTJSDwS7Ckt"
300 | },
301 | "outputs": [],
302 | "source": [
303 | "# Pull the managed configurations of the application to edit in the next code snippet below\n",
304 | "\n",
305 | "application = androidmanagement.enterprises().applications().get(\n",
306 | " name= enterprise_name + '/applications/' + package_name\n",
307 | ").execute()\n",
308 | "\n",
309 | "prettyprint(application[\"managedProperties\"])"
310 | ]
311 | },
312 | {
313 | "cell_type": "markdown",
314 | "metadata": {
315 | "id": "mfsM_AGh8Ycm"
316 | },
317 | "source": [
318 | "Create your own UI and apply the settings via managedConfiguration in ApplicationPolicy."
319 | ]
320 | },
321 | {
322 | "cell_type": "code",
323 | "execution_count": null,
324 | "metadata": {
325 | "id": "T0BGwYilq_HC"
326 | },
327 | "outputs": [],
328 | "source": [
329 | "policy_name = enterprise_name + '/policies/managed-configurations'\n",
330 | "\n",
331 | "policy = {\n",
332 | " 'applications': [\n",
333 | " {\n",
334 | " 'installType': 'FORCE_INSTALLED',\n",
335 | " 'packageName': package_name,\n",
336 | " 'managedConfiguration': {\n",
337 | " # Enter your configuration using this key/value pair template.\n",
338 | " 'key1': 'value1',\n",
339 | " 'key2': 'value2'\n",
340 | " }\n",
341 | " }\n",
342 | " ],\n",
343 | " 'debuggingFeaturesAllowed': 'true'\n",
344 | "}\n",
345 | "\n",
346 | "# Write the new policy:\n",
347 | "androidmanagement.enterprises().policies().patch(\n",
348 | " name=policy_name,\n",
349 | " body=policy\n",
350 | ").execute()"
351 | ]
352 | },
353 | {
354 | "cell_type": "markdown",
355 | "metadata": {
356 | "id": "3Sf3qQL9CrIG"
357 | },
358 | "source": [
359 | "# Retrieve feedback from the configured app\n",
360 | "\n",
361 | "This section shows you how to [retrieve feedback from apps](https://developers.google.com/android/management/app-feedback) through the Android Management API. This communication channel allows IT admins to receive feedback about the status of the apps installed on the devices they manage.\n"
362 | ]
363 | },
364 | {
365 | "cell_type": "markdown",
366 | "metadata": {
367 | "id": "PnWwzIFyJXQS"
368 | },
369 | "source": [
370 | "\n",
371 | "Below is an example of how to create or update a `policy` for apps on a device with settings controlling the behavior of status reports. To enable reporting for a device, update the device policy and set StatusReportingSettings.applicationReportsEnabled to true. To enable device settings reporting set StatusReportingSettings.deviceSettingsEnabled to true.\n",
372 | "\n"
373 | ]
374 | },
375 | {
376 | "cell_type": "code",
377 | "execution_count": null,
378 | "metadata": {
379 | "id": "mJ-PFORwdB4A"
380 | },
381 | "outputs": [],
382 | "source": [
383 | "# Update a policy without replacing the existing policy.\n",
384 | "\n",
385 | "policy_name = enterprise_name + '/policies/managed-configurations'\n",
386 | "\n",
387 | "policy = {\n",
388 | " 'statusReportingSettings' : {\n",
389 | " 'applicationReportsEnabled': True\n",
390 | " }\n",
391 | "}\n",
392 | "\n",
393 | "androidmanagement.enterprises().policies().patch(\n",
394 | " name=policy_name,\n",
395 | " updateMask='statusReportingSettings.applicationReportsEnabled',\n",
396 | " body=policy\n",
397 | ").execute()"
398 | ]
399 | },
400 | {
401 | "cell_type": "code",
402 | "execution_count": null,
403 | "metadata": {
404 | "id": "ofbcBL5NmSIW"
405 | },
406 | "outputs": [],
407 | "source": [
408 | "# List devices so you can get a device name to use to pull app feedback below.\n",
409 | "androidmanagement.enterprises().devices().list(\n",
410 | " parent=enterprise_name\n",
411 | ").execute()"
412 | ]
413 | },
414 | {
415 | "cell_type": "markdown",
416 | "metadata": {
417 | "id": "CGQSWpfP9tWD"
418 | },
419 | "source": [
420 | "To review a devices's latest report at any time, call [devices.get()](https://developers.google.com/android/management/reference/rest/v1/enterprises.devices/get) \n",
421 | "\n",
422 | "IT admins can use the data from keyed app states to retrieve information about device states."
423 | ]
424 | },
425 | {
426 | "cell_type": "code",
427 | "execution_count": null,
428 | "metadata": {
429 | "id": "roqlhKC0EDrI"
430 | },
431 | "outputs": [],
432 | "source": [
433 | "# Enter the device name\n",
434 | "device_name = ''"
435 | ]
436 | },
437 | {
438 | "cell_type": "code",
439 | "execution_count": null,
440 | "metadata": {
441 | "id": "TOnOsZtOmHlw"
442 | },
443 | "outputs": [],
444 | "source": [
445 | "# Review a device’s latest report:\n",
446 | "androidmanagement.enterprises().devices().get(\n",
447 | " name=device_name\n",
448 | ").execute()"
449 | ]
450 | },
451 | {
452 | "cell_type": "markdown",
453 | "metadata": {
454 | "id": "jTyUXuvfxAFP"
455 | },
456 | "source": [
457 | "# Set up [Pub/Sub notifications](https://developers.google.com/android/management/notifications)\n",
458 | "\n",
459 | "As an alternative you can set up Pub/Sub notifications to receive alerts about newly enrolled devices, device reports, and recently issued commands. To set up Pub/Sub notifications, you need to enable the Pub/Sub API and create a topic. To receive messages published to a topic, create a subscription to that topic. After you create a subscription, you need to grant Android Device Policy permission to publish to your topic.\n",
460 | "\n",
461 | "The set up link above will guide you on how to: \n",
462 | "\n",
463 | "1. Enable the Pub/Sub API for your project\n",
464 | "2. Create a topic\n",
465 | "3. Create a subscription\n",
466 | "4. Grant Android Device Policy the right to publish to your topic\n",
467 | "5. Update an enterprise to support notifications\n",
468 | "\n",
469 | "Go to your [Cloud Console](https://console.cloud.google.com/cloud-resource-manager) to get your project ID."
470 | ]
471 | },
472 | {
473 | "cell_type": "code",
474 | "execution_count": null,
475 | "metadata": {
476 | "id": "2DEmZ5Wcn4et"
477 | },
478 | "outputs": [],
479 | "source": [
480 | "# Enter your Google Cloud Project ID below:\n",
481 | "cloud_project_id = ''"
482 | ]
483 | },
484 | {
485 | "cell_type": "code",
486 | "execution_count": null,
487 | "metadata": {
488 | "id": "R-Ow14HOoNc2"
489 | },
490 | "outputs": [],
491 | "source": [
492 | "# Go to the Pub/Sub topics page in the Cloud Console to get the topic name you created.\n",
493 | "# Enter the topic you created below:\n",
494 | "my_topic = ''"
495 | ]
496 | },
497 | {
498 | "cell_type": "code",
499 | "execution_count": null,
500 | "metadata": {
501 | "id": "Hc_rm5nfQWgD"
502 | },
503 | "outputs": [],
504 | "source": [
505 | "\n",
506 | "# Update the enterprise to support notifications:\n",
507 | "androidmanagement.enterprises().patch(\n",
508 | " name=enterprise_name,\n",
509 | " body={\n",
510 | " \"name\" : enterprise_name,\n",
511 | " \"enabledNotificationTypes\" : \"STATUS_REPORT\",\n",
512 | " \"pubsubTopic\": \"projects/\" + cloud_project_id + \"/topics/\" + my_topic\n",
513 | " }\n",
514 | ").execute()"
515 | ]
516 | }
517 | ],
518 | "metadata": {
519 | "colab": {
520 | "collapsed_sections": [],
521 | "name": "managed_configurations.ipynb",
522 | "provenance": [],
523 | "toc_visible": true
524 | },
525 | "kernelspec": {
526 | "display_name": "Python 3",
527 | "name": "python3"
528 | }
529 | },
530 | "nbformat": 4,
531 | "nbformat_minor": 0
532 | }
533 |
--------------------------------------------------------------------------------
/notebooks/quickstart.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {
6 | "id": "lbI8y3v1G6OM"
7 | },
8 | "source": [
9 | "#### Copyright 2022 Google LLC.\n",
10 | "\n",
11 | "Licensed under the Apache License, Version 2.0 (the \"License\");"
12 | ]
13 | },
14 | {
15 | "cell_type": "code",
16 | "execution_count": null,
17 | "metadata": {
18 | "id": "JxL-8R6KHRtK"
19 | },
20 | "outputs": [],
21 | "source": [
22 | "# Licensed under the Apache License, Version 2.0 (the \"License\");\n",
23 | "# you may not use this file except in compliance with the License.\n",
24 | "# You may obtain a copy of the License at\n",
25 | "\n",
26 | "# https://www.apache.org/licenses/LICENSE-2.0\n",
27 | "\n",
28 | "# Unless required by applicable law or agreed to in writing, software\n",
29 | "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
30 | "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
31 | "# See the License for the specific language governing permissions and\n",
32 | "# limitations under the License."
33 | ]
34 | },
35 | {
36 | "cell_type": "markdown",
37 | "metadata": {
38 | "id": "3Sf3qQL9CrIG"
39 | },
40 | "source": [
41 | "# Android Management API - Quickstart\n",
42 | "\n",
43 | "This notebook shows you how to get started with the Android Management API. Follow\n",
44 | "the steps below to enroll an enterprise, create a policy, and provision a device.\n",
45 | "\n",
46 | "In order to run this notebook, you need:\n",
47 | "\n",
48 | "* An Android 6.0+ device.\n",
49 | "* A Gmail account. This account cannot be associated with an existing enterprise.\n",
50 | "\n",
51 | "## Setup\n",
52 | "\n",
53 | "The base resource of your Android Management solution is a Google Cloud Platform project. All other resources (`Enterprises`, `Devices`, `Policies`, etc) belong to the project and the project controls access to these resources. A solution is typically associated with a single project, but you can create multiple projects if you want to restrict access to resources.\n",
54 | "\n",
55 | "You can create a project in the Google Cloud Console:\n",
56 | "\n",
57 | "1. [**Go to the Cloud Console**](https://console.cloud.google.com/cloud-resource-manager).\n",
58 | "2. Click **CREATE PROJECT**.\n",
59 | "3. Enter your project details, and then click **CREATE**.\n",
60 | "2. Take note of the **project ID** and **paste it in the cell below**, then **run the cell**.\n",
61 | "\n",
62 | "To run a cell:\n",
63 | "\n",
64 | "1. Click anywhere in the code block.\n",
65 | "2. Click the \u0026#9654; button in the top-left of the code block.\n"
66 | ]
67 | },
68 | {
69 | "cell_type": "code",
70 | "execution_count": null,
71 | "metadata": {
72 | "id": "orawejuC9GHB"
73 | },
74 | "outputs": [],
75 | "source": [
76 | "# Paste your project ID here.\n",
77 | "cloud_project_id = ''"
78 | ]
79 | },
80 | {
81 | "cell_type": "markdown",
82 | "metadata": {
83 | "id": "n3FaCG1wJAO7"
84 | },
85 | "source": [
86 | "To create and access resources, you need to authenticate with an account that has edit rights over your project. To start the authentication flow, **run the cell below**.\n",
87 | "\n",
88 | "When you build a server-based solution, you should create a\n",
89 | "[service account](https://developers.google.com/android/management/service-account)\n",
90 | "so you don't need to authorize the access every time."
91 | ]
92 | },
93 | {
94 | "cell_type": "code",
95 | "execution_count": null,
96 | "metadata": {
97 | "id": "8myK1D6uy7HR"
98 | },
99 | "outputs": [],
100 | "source": [
101 | "from apiclient.discovery import build\n",
102 | "from google_auth_oauthlib.flow import Flow\n",
103 | "\n",
104 | "# This is a public OAuth config that you can use to run this guide.\n",
105 | "# However, use different credentials when building your own solution.\n",
106 | "CLIENT_CONFIG = {\n",
107 | " 'web': {\n",
108 | " 'client_id':'882252295571-m8qpit609pj2hsql4j5k0bue1injrtd2.apps.googleusercontent.com',\n",
109 | " 'auth_uri':'https://accounts.google.com/o/oauth2/auth',\n",
110 | " 'token_uri':'https://oauth2.googleapis.com/token',\n",
111 | " 'auth_provider_x509_cert_url':'https://www.googleapis.com/oauth2/v1/certs',\n",
112 | " 'client_secret':'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'\n",
113 | " }\n",
114 | "}\n",
115 | "SCOPES = ['https://www.googleapis.com/auth/androidmanagement']\n",
116 | "CALLBACK_URL = 'https://google.github.io/android-management-api-samples/oauth_callback.html'\n",
117 | "\n",
118 | "# Run the OAuth flow.\n",
119 | "flow = Flow.from_client_config(CLIENT_CONFIG, SCOPES)\n",
120 | "flow.redirect_uri = CALLBACK_URL\n",
121 | "auth_url, _ = flow.authorization_url()\n",
122 | "print('Please visit this URL to authorize this application: {}'.format(auth_url))\n",
123 | "\n",
124 | "code = input('Enter the authorization code: ')\n",
125 | "flow.fetch_token(code=code)\n",
126 | "\n",
127 | "# Create the API client.\n",
128 | "androidmanagement = build('androidmanagement', 'v1', credentials=flow.credentials)\n",
129 | "\n",
130 | "print('\\nAuthentication succeeded.')"
131 | ]
132 | },
133 | {
134 | "cell_type": "markdown",
135 | "metadata": {
136 | "id": "bsnuDBT7JQ9O"
137 | },
138 | "source": [
139 | "## Create an enterprise\n",
140 | "\n",
141 | "An **`Enterprise`** resource binds an organization to your Android Management solution.\n",
142 | "**`Devices`** and **`Policies`** both belong to an enterprise. Typically, a single enterprise\n",
143 | "resource is associated with a single organization. However, you can create multiple\n",
144 | "enterprises for the same organization based on their needs. For example, an\n",
145 | "organization may want separate enterprises for its different departments or regions.\n",
146 | "\n",
147 | "To create an enterprise you need a **Gmail account** that's not already associated with\n",
148 | "an enterprise.\n",
149 | "\n",
150 | "To start the enterprise creation flow, **run the cell below**.\n",
151 | "\n",
152 | "If you've already created an enterprise for this project, you can skip this step and enter your enterprise name in the next cell. "
153 | ]
154 | },
155 | {
156 | "cell_type": "code",
157 | "execution_count": null,
158 | "metadata": {
159 | "id": "TSeTmD7Y85NQ"
160 | },
161 | "outputs": [],
162 | "source": [
163 | "CALLBACK_URL = 'https://storage.googleapis.com/android-management-quick-start/enterprise_signup_callback.html'\n",
164 | "\n",
165 | "# Generate a signup URL where the enterprise admin can signup with a Gmail\n",
166 | "# account.\n",
167 | "signup_url = androidmanagement.signupUrls().create(\n",
168 | " projectId=cloud_project_id,\n",
169 | " callbackUrl=CALLBACK_URL\n",
170 | ").execute()\n",
171 | "\n",
172 | "print('Please visit this URL to create an enterprise:', signup_url['url'])\n",
173 | "\n",
174 | "enterprise_token = input('Enter the code: ')\n",
175 | "\n",
176 | "# Complete the creation of the enterprise and retrieve the enterprise name.\n",
177 | "enterprise = androidmanagement.enterprises().create(\n",
178 | " projectId=cloud_project_id,\n",
179 | " signupUrlName=signup_url['name'],\n",
180 | " enterpriseToken=enterprise_token,\n",
181 | " body={}\n",
182 | ").execute()\n",
183 | "\n",
184 | "enterprise_name = enterprise['name']\n",
185 | "\n",
186 | "print('\\nYour enterprise name is', enterprise_name)"
187 | ]
188 | },
189 | {
190 | "cell_type": "markdown",
191 | "metadata": {
192 | "id": "PnWwzIFyJXQS"
193 | },
194 | "source": [
195 | "Take note of the enterprise name so you can reuse it after you close this notebook.\n",
196 | "\n",
197 | "If you already have an enterprise, you can enter the enterprise name in the cell below and run the cell."
198 | ]
199 | },
200 | {
201 | "cell_type": "code",
202 | "execution_count": null,
203 | "metadata": {
204 | "id": "R4rNq_O_BNBJ"
205 | },
206 | "outputs": [],
207 | "source": [
208 | "# Paste your enterprise name here.\n",
209 | "enterprise_name = ''"
210 | ]
211 | },
212 | {
213 | "cell_type": "markdown",
214 | "metadata": {
215 | "id": "urkbkf7zJgqN"
216 | },
217 | "source": [
218 | "## Create a policy\n",
219 | "\n",
220 | "A **`Policy`** is a group of settings that determine the behavior of a managed device\n",
221 | "and the apps installed on it. Each Policy resource represents a unique group of device\n",
222 | "and app settings and can be applied to one or more devices. Once a device is linked to\n",
223 | "a policy, any updates to the policy are automatically applied to the device.\n",
224 | "\n",
225 | "To create a basic policy, **run the cell below**. You'll see how to create more advanced policies later in this guide."
226 | ]
227 | },
228 | {
229 | "cell_type": "code",
230 | "execution_count": null,
231 | "metadata": {
232 | "id": "pjHfDSb8BoBP"
233 | },
234 | "outputs": [],
235 | "source": [
236 | "import json\n",
237 | "\n",
238 | "policy_name = enterprise_name + '/policies/policy1'\n",
239 | "\n",
240 | "policy_json = '''\n",
241 | "{\n",
242 | " \"applications\": [\n",
243 | " {\n",
244 | " \"packageName\": \"com.google.samples.apps.iosched\",\n",
245 | " \"installType\": \"FORCE_INSTALLED\"\n",
246 | " }\n",
247 | " ],\n",
248 | " \"advancedSecurityOverrides\": {\n",
249 | " \"developerSettings\": \"DEVELOPER_SETTINGS_ALLOWED\"\n",
250 | " }\n",
251 | "}\n",
252 | "'''\n",
253 | "\n",
254 | "androidmanagement.enterprises().policies().patch(\n",
255 | " name=policy_name,\n",
256 | " body=json.loads(policy_json)\n",
257 | ").execute()"
258 | ]
259 | },
260 | {
261 | "cell_type": "markdown",
262 | "metadata": {
263 | "id": "SyxrXcIMJuDF"
264 | },
265 | "source": [
266 | "## Provision a device\n",
267 | "\n",
268 | "Provisioning refers to the process of enrolling a device with an enterprise, applying the appropriate policies to the device, and guiding the user to complete the set up of their device in accordance with those policies. Before attempting to provision a device, ensure that the device is running Android 6.0 or above.\n",
269 | "\n",
270 | "You need an enrollment token for each device that you want to provision (you can use the same token for multiple devices), when creating a token you can specify a policy that will be applied to the device."
271 | ]
272 | },
273 | {
274 | "cell_type": "code",
275 | "execution_count": null,
276 | "metadata": {
277 | "id": "JPYgBZSmCEil"
278 | },
279 | "outputs": [],
280 | "source": [
281 | "enrollment_token = androidmanagement.enterprises().enrollmentTokens().create(\n",
282 | " parent=enterprise_name,\n",
283 | " body={\"policyName\": policy_name}\n",
284 | ").execute()"
285 | ]
286 | },
287 | {
288 | "cell_type": "markdown",
289 | "metadata": {
290 | "id": "zmq6yvGsJ3pJ"
291 | },
292 | "source": [
293 | "Embed your enrollment token in either an enrollment link or a QR code, and then follow the provisioning instructions below."
294 | ]
295 | },
296 | {
297 | "cell_type": "code",
298 | "execution_count": null,
299 | "metadata": {
300 | "id": "TTSgnpC9EScx"
301 | },
302 | "outputs": [],
303 | "source": [
304 | "from IPython.display import display, HTML\n",
305 | "\n",
306 | "qr_code_content = enrollment_token['qrCode']\n",
307 | "qr_code_settings = {\n",
308 | " 'text': qr_code_content,\n",
309 | " 'width': 500,\n",
310 | " 'height': 500\n",
311 | "}\n",
312 | "\n",
313 | "display(HTML('''\n",
314 | " \u003cdiv id=\"qr_code\" style=\"padding: 16px; background: #FFF; width: 500px;\"\u003e\u003c/div\u003e\n",
315 | " \u003cscript type=\"text/javascript\" src=\"//cdn.rawgit.com/davidshimjs/qrcodejs/gh-pages/qrcode.min.js\"\u003e\u003c/script\u003e\n",
316 | " \u003cscript type=\"text/javascript\"\u003e\n",
317 | " new QRCode(document.getElementById(\"qr_code\"), {});\n",
318 | " \u003c/script\u003e\n",
319 | "'''.format(qr_code_settings)))"
320 | ]
321 | },
322 | {
323 | "cell_type": "code",
324 | "execution_count": null,
325 | "metadata": {
326 | "id": "7ICRh_3UsiU4"
327 | },
328 | "outputs": [],
329 | "source": [
330 | "enrollment_link = 'https://enterprise.google.com/android/enroll?et=' + enrollment_token['value']\n",
331 | "\n",
332 | "print('Please open this link on your device:', enrollment_link)"
333 | ]
334 | },
335 | {
336 | "cell_type": "markdown",
337 | "metadata": {
338 | "id": "hcdeKcrIfM5H"
339 | },
340 | "source": [
341 | "The method for provisioning a device varies depending on the management mode you want to use.\n",
342 | "\n",
343 | "### Fully managed mode\n",
344 | "\n",
345 | "In **fully managed mode** the entire device is managed and the device needs to be factory reset before setup. To set up a device in fully managed mode you need to use a QR code.\n",
346 | "\n",
347 | "For devices running Android 7.0 or above:\n",
348 | "\n",
349 | "1. Turn on a new or factory-reset device.\n",
350 | "2. Tap the same spot on the welcome screen six times to enter QR code mode.\n",
351 | "3. Connect to a WiFi network.\n",
352 | "4. Scan the QR code.\n",
353 | "\n",
354 | "For devices running Android 6.0:\n",
355 | "\n",
356 | "1. Turn on a new or factory-reset device.\n",
357 | "2. Follow the setup wizard and enter your Wi-Fi details.\n",
358 | "3. When prompted to sign in, enter **afw#setup**.\n",
359 | "4. Tap Next, and then accept the installation of Android Device Policy.\n",
360 | "5. Scan the QR code.\n",
361 | "\n",
362 | "### Work profile mode\n",
363 | "\n",
364 | "In **work profile mode** corporate apps and data are kept secure in a self-contained work profile while the user keeps control of the rest of the device. To set up a work profile you can either use a QR code or an enrollment link.\n",
365 | "\n",
366 | "Using the enrollment link:\n",
367 | "\n",
368 | "1. Make the link accessible on the device (send it via email or put it on a website).\n",
369 | "2. Open the link.\n",
370 | "\n",
371 | "Or using the QR code:\n",
372 | "\n",
373 | "1. Go to **Settings** \u003e **Google**.\n",
374 | "2. Tap \"Set up your work profile\".\n",
375 | "3. Scan the QR code.\n",
376 | "\n",
377 | "## What's next?\n",
378 | "\n",
379 | "By now you should have a managed device configured with a basic policy, but there's\n",
380 | "much more you can do with the Android Management API.\n",
381 | "\n",
382 | "First, we recommend\n",
383 | "[**exploring the range of available policies**](https://developers.google.com/android/management/create-policy)\n",
384 | "to build the right policy for your needs. \n",
385 | "\n",
386 | "Next, explore other features of the Android Management API:\n",
387 | "\n",
388 | "* Learn how to [discover apps](https://developers.google.com/android/management/apps)\n",
389 | "* Set up [Pub/Sub notifications](https://developers.google.com/android/management/notifications)\n",
390 | "\n",
391 | "Or start developing a server-based solution:\n",
392 | "\n",
393 | "* Download the Android Management API client library for\n",
394 | " [Java](https://developers.google.com/api-client-library/java/apis/androidmanagement/v1),\n",
395 | " [.NET](https://developers.google.com/api-client-library/dotnet/apis/androidmanagement/v1),\n",
396 | " [Python](https://developers.google.com/api-client-library/python/apis/androidmanagement/v1),\n",
397 | " or [Ruby](https://developers.google.com/api-client-library/ruby/apis/androidmanagement/v1).\n",
398 | "* Create a [service account](https://developers.google.com/android/management/service-account).\n",
399 | "\n"
400 | ]
401 | }
402 | ],
403 | "metadata": {
404 | "colab": {
405 | "collapsed_sections": [
406 | "lbI8y3v1G6OM"
407 | ],
408 | "name": "Android Management API - Quickstart",
409 | "private_outputs": true,
410 | "provenance": [],
411 | "toc_visible": true
412 | },
413 | "kernelspec": {
414 | "display_name": "Python 3",
415 | "name": "python3"
416 | }
417 | },
418 | "nbformat": 4,
419 | "nbformat_minor": 0
420 | }
421 |
--------------------------------------------------------------------------------
/notebooks/web_apps.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {
6 | "id": "lbI8y3v1G6OM"
7 | },
8 | "source": [
9 | "#### Copyright 2022 Google LLC.\n",
10 | "\n",
11 | "Licensed under the Apache License, Version 2.0 (the \"License\");"
12 | ]
13 | },
14 | {
15 | "cell_type": "code",
16 | "execution_count": null,
17 | "metadata": {
18 | "id": "JxL-8R6KHRtK"
19 | },
20 | "outputs": [],
21 | "source": [
22 | "# Licensed under the Apache License, Version 2.0 (the \"License\");\n",
23 | "# you may not use this file except in compliance with the License.\n",
24 | "# You may obtain a copy of the License at\n",
25 | "\n",
26 | "# https://www.apache.org/licenses/LICENSE-2.0\n",
27 | "\n",
28 | "# Unless required by applicable law or agreed to in writing, software\n",
29 | "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
30 | "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
31 | "# See the License for the specific language governing permissions and\n",
32 | "# limitations under the License."
33 | ]
34 | },
35 | {
36 | "cell_type": "markdown",
37 | "metadata": {
38 | "id": "3Sf3qQL9CrIG"
39 | },
40 | "source": [
41 | "# Android Management API - Quickstart for Web Apps\n",
42 | "\n",
43 | "This notebook shows you how to create and manage web apps with the Android Management API. \n",
44 | "It is assumed that you have worked through the [Android Management API quickstart guide](https://developers.google.com/android/management/quickstart) and are therefore familiar with how to use this colab notebook and already have a cloud project, test enterprise, and provisioned device.\n",
45 | "\n",
46 | "\n",
47 | "## Authenticating\n",
48 | "To create and access resources, you need to authenticate with an account that has edit rights over your project. To start the authentication flow, **run the cell below**."
49 | ]
50 | },
51 | {
52 | "cell_type": "code",
53 | "execution_count": null,
54 | "metadata": {
55 | "id": "8myK1D6uy7HR"
56 | },
57 | "outputs": [],
58 | "source": [
59 | "import json\n",
60 | "from apiclient.discovery import build\n",
61 | "from google_auth_oauthlib.flow import Flow\n",
62 | "import pprint\n",
63 | "\n",
64 | "# This is a public OAuth config that you can use to run this guide.\n",
65 | "# However, use different credentials when building your own solution.\n",
66 | "CLIENT_CONFIG = {\n",
67 | " 'web': {\n",
68 | " 'client_id':'882252295571-m8qpit609pj2hsql4j5k0bue1injrtd2.apps.googleusercontent.com',\n",
69 | " 'auth_uri':'https://accounts.google.com/o/oauth2/auth',\n",
70 | " 'token_uri':'https://oauth2.googleapis.com/token',\n",
71 | " 'auth_provider_x509_cert_url':'https://www.googleapis.com/oauth2/v1/certs',\n",
72 | " 'client_secret':'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'\n",
73 | " }\n",
74 | "}\n",
75 | "SCOPES = ['https://www.googleapis.com/auth/androidmanagement']\n",
76 | "CALLBACK_URL = 'https://google.github.io/android-management-api-samples/oauth_callback.html'\n",
77 | "\n",
78 | "# Run the OAuth flow.\n",
79 | "flow = Flow.from_client_config(CLIENT_CONFIG, SCOPES)\n",
80 | "flow.redirect_uri = CALLBACK_URL\n",
81 | "auth_url, _ = flow.authorization_url()\n",
82 | "print('Please visit this URL to authorize this application: {}'.format(auth_url))\n",
83 | "\n",
84 | "code = input('Enter the authorization code: ')\n",
85 | "flow.fetch_token(code=code)\n",
86 | "\n",
87 | "# Create the API client.\n",
88 | "androidmanagement = build('androidmanagement', 'v1', credentials=flow.credentials)\n",
89 | "\n",
90 | "print('\\nAuthentication succeeded.')\n",
91 | "\n",
92 | "# To improve the readability of deeply nested data structures, we create a \n",
93 | "# helper function to pretty print a result:\n",
94 | "prettyprint = pprint.PrettyPrinter(indent=4).pprint\n"
95 | ]
96 | },
97 | {
98 | "cell_type": "markdown",
99 | "metadata": {
100 | "id": "bsnuDBT7JQ9O"
101 | },
102 | "source": [
103 | "## Declare your enterprise\n",
104 | "In the [general Quickstart](https://developers.google.com/android/management/quickstart) you already created a test enterprise which will be used here."
105 | ]
106 | },
107 | {
108 | "cell_type": "code",
109 | "execution_count": null,
110 | "metadata": {
111 | "id": "R4rNq_O_BNBJ"
112 | },
113 | "outputs": [],
114 | "source": [
115 | "# Paste your enterprise name here.\n",
116 | "# It should be of the format 'enterprises/LC...'\n",
117 | "enterprise_name = ''"
118 | ]
119 | },
120 | {
121 | "cell_type": "markdown",
122 | "metadata": {
123 | "id": "W6H-nD3uT6no"
124 | },
125 | "source": [
126 | "# Create a web app with the managed Google Play iframe\n",
127 | "\n",
128 | "The managed Google Play iframe includes a user interface for IT admins to create, edit, and delete web apps.\n",
129 | "\n",
130 | "**Run the cell below** to open the managed Google Play iframe.\n",
131 | "\n",
132 | "In the iframe:\n",
133 | "* In the left navigation menu click **Web apps**\n",
134 | "* Click the **+** button\n",
135 | "* Enter a title and a URL, select a display mode, and optionally upload an icon \n",
136 | "* Click **Create**\n",
137 | "* In the web app list click on the web app you've just created\n",
138 | "* Click **Select**\n",
139 | "* Copy the package name of the web app and add it in the policy below\n",
140 | "\n"
141 | ]
142 | },
143 | {
144 | "cell_type": "code",
145 | "execution_count": null,
146 | "metadata": {
147 | "id": "Q_1V5jbCUGWS"
148 | },
149 | "outputs": [],
150 | "source": [
151 | "IFRAME_URL = \"https://storage.googleapis.com/android-management-api-samples/managed_play_iframe.html\"\n",
152 | "\n",
153 | "web_token = androidmanagement.enterprises().webTokens().create(\n",
154 | " parent=enterprise_name,\n",
155 | " body={\n",
156 | " \"parentFrameUrl\": IFRAME_URL\n",
157 | " }\n",
158 | ").execute()\n",
159 | "\n",
160 | "full_iframe_url = IFRAME_URL + \"?mode=SELECT\u0026token=\" + web_token[\"value\"]\n",
161 | "\n",
162 | "print('Open the managed Google Play iframe:', full_iframe_url)"
163 | ]
164 | },
165 | {
166 | "cell_type": "markdown",
167 | "metadata": {
168 | "id": "_9uLoHmVs3wi"
169 | },
170 | "source": [
171 | "# Distributing the web app\n",
172 | "\n",
173 | "Once a web app is created, it can be used just like a normal app, e.g. added to a policy. Assuming you already used the policy `/policies/policy1` from the [Android Management API quickstart guide](https://developers.google.com/android/management/quickstart) when you set up the test device, we will use that name.\n",
174 | "\n",
175 | "**Important**: For a device to support web apps, Google Chrome must be installed. When distributing web apps, ensure that Google Chrome (`com.android.chrome`) is always included in a device's policy.\n",
176 | "\n"
177 | ]
178 | },
179 | {
180 | "cell_type": "code",
181 | "execution_count": null,
182 | "metadata": {
183 | "id": "T_AAKoqVs6ae"
184 | },
185 | "outputs": [],
186 | "source": [
187 | "policy_name = enterprise_name + '/policies/policy1'\n",
188 | "\n",
189 | "# Paste the package name of the web app here\n",
190 | "# It should be of the format 'com.google.enterprise.webapp.*'.\n",
191 | "web_app_package_name = ''\n",
192 | "\n",
193 | "policy = {\n",
194 | " 'applications': [\n",
195 | " {\n",
196 | " 'installType': 'FORCE_INSTALLED',\n",
197 | " 'packageName': 'com.android.chrome'\n",
198 | " },\n",
199 | " {\n",
200 | " 'installType': 'FORCE_INSTALLED',\n",
201 | " 'packageName': web_app_package_name\n",
202 | " }\n",
203 | " ],\n",
204 | " 'debuggingFeaturesAllowed': True\n",
205 | "}\n",
206 | "\n",
207 | "# Write the new policy:\n",
208 | "result = androidmanagement.enterprises().policies().patch(\n",
209 | " name=policy_name,\n",
210 | " body=policy\n",
211 | ").execute()\n",
212 | "\n",
213 | "prettyprint(result)"
214 | ]
215 | },
216 | {
217 | "cell_type": "markdown",
218 | "metadata": {
219 | "id": "HkBhR0EiVvYW"
220 | },
221 | "source": [
222 | "# Create a web app with the Android Management API\n",
223 | "\n",
224 | "Another way to support web apps is to use the Android Management API to create and manage web apps."
225 | ]
226 | },
227 | {
228 | "cell_type": "markdown",
229 | "metadata": {
230 | "id": "b6ad2CvRXddq"
231 | },
232 | "source": [
233 | "## enterprises.webApps.create\n",
234 | "\n",
235 | "You can use the method [`enterprises.webApps.create`](https://developers.google.com/android/management/reference/rest/v1/enterprises.webApps/create) to create a web app. You need just a title and a URL, plus the display mode (can be one of `MINIMAL_UI`, `STANDALONE`, or `FULL_SCREEN`).\n",
236 | "\n",
237 | "While an icon is optional, it's best practice to always specify a meaningful icon for a web app. Like a title, an icon helps users identify an app. Google applies the same fixed static icon for all web apps that don't have a specified icon.\n",
238 | "\n",
239 | "The icon data needs to be encoded in base64url (see [RFC 4648, section-5](https://tools.ietf.org/html/rfc4648#section-5)). It should be a square (or near-square) shape, ideally around 512x512 pixels."
240 | ]
241 | },
242 | {
243 | "cell_type": "code",
244 | "execution_count": null,
245 | "metadata": {
246 | "id": "Vyg8dItnWFpa"
247 | },
248 | "outputs": [],
249 | "source": [
250 | "import base64\n",
251 | "import urllib.request\n",
252 | "\n",
253 | "# A useful picture found on the Wikipedia page:\n",
254 | "icon_url = 'https://developer.android.com/images/brand/Android_Robot.png'\n",
255 | "\n",
256 | "icon_raw_data = urllib.request.urlopen(icon_url).read()\n",
257 | "icon_encoded_data = base64.urlsafe_b64encode(icon_raw_data).decode(\"utf-8\")\n",
258 | "\n",
259 | "web_app = {\n",
260 | " \"title\": \"Android\",\n",
261 | " \"startUrl\": \"https://en.m.wikipedia.org/wiki/Android_(operating_system)\",\n",
262 | " \"displayMode\": \"MINIMAL_UI\",\n",
263 | " \"icons\": [\n",
264 | " {\n",
265 | " \"imageData\": icon_encoded_data\n",
266 | " }\n",
267 | " ]\n",
268 | "}\n",
269 | "\n",
270 | "result = androidmanagement.enterprises().webApps().create(\n",
271 | " parent=enterprise_name,\n",
272 | " body=web_app\n",
273 | ").execute()\n",
274 | "\n",
275 | "# Take note of the app's name (which is it's id) for future operations:\n",
276 | "web_app_name = result['name']\n",
277 | "\n",
278 | "prettyprint(result)"
279 | ]
280 | },
281 | {
282 | "cell_type": "markdown",
283 | "metadata": {
284 | "id": "kKeHN5yzo09F"
285 | },
286 | "source": [
287 | "The result of the create call basically mirrors its inputs but with the icon data and the name filled in.\n",
288 | "\n",
289 | "For later use, the `name` component of the result is the imporant piece of information, which should be extracted. A web app's package name is the last component of its name, i.e. the part after the last slash (`/`) character."
290 | ]
291 | },
292 | {
293 | "cell_type": "markdown",
294 | "metadata": {
295 | "id": "ufsD2hfkpVlg"
296 | },
297 | "source": [
298 | "## enterprises.webApps.patch\n",
299 | "\n",
300 | "With the `enterprises.webApps.patch` call you can update any aspect of a web app.\n",
301 | "\n",
302 | "E.g. here we change the title and display mode."
303 | ]
304 | },
305 | {
306 | "cell_type": "code",
307 | "execution_count": null,
308 | "metadata": {
309 | "id": "qjundP3YqYNd"
310 | },
311 | "outputs": [],
312 | "source": [
313 | "web_app = {\n",
314 | " \"title\": \"Wikipedia: Android\",\n",
315 | " \"startUrl\": \"https://en.m.wikipedia.org/wiki/Android_(operating_system)\",\n",
316 | " \"displayMode\": \"STANDALONE\",\n",
317 | " \"icons\": [\n",
318 | " {\n",
319 | " \"imageData\": icon_encoded_data\n",
320 | " }\n",
321 | " ]\n",
322 | "}\n",
323 | "\n",
324 | "result = androidmanagement.enterprises().webApps().patch(\n",
325 | " name=web_app_name,\n",
326 | " body=web_app\n",
327 | ").execute()\n",
328 | "\n",
329 | "prettyprint(result)"
330 | ]
331 | },
332 | {
333 | "cell_type": "markdown",
334 | "metadata": {
335 | "id": "tf6VE8xapJE4"
336 | },
337 | "source": [
338 | "## enterprises.webApps.list\n",
339 | "\n",
340 | "The `enterprises.webApps.list` method returns information about all web apps. \n",
341 | "\n",
342 | "This is also a handy way to recover the name (aka the id) of a web app should it be forgotten after creation."
343 | ]
344 | },
345 | {
346 | "cell_type": "code",
347 | "execution_count": null,
348 | "metadata": {
349 | "id": "60oVxrxpg-Zr"
350 | },
351 | "outputs": [],
352 | "source": [
353 | "webapps = androidmanagement.enterprises().webApps().list(\n",
354 | " parent=enterprise_name\n",
355 | ").execute()\n",
356 | "\n",
357 | "prettyprint(webapps)"
358 | ]
359 | },
360 | {
361 | "cell_type": "markdown",
362 | "metadata": {
363 | "id": "OtWosifKk5VT"
364 | },
365 | "source": [
366 | "## enterprises.webApps.get\n",
367 | "\n",
368 | "To get details about a single web app, the `enterprises.webApps.get` call is used.\n",
369 | "\n",
370 | "*Note* that data from the list call does not contain information about the icons. To get that, the get is is needed."
371 | ]
372 | },
373 | {
374 | "cell_type": "code",
375 | "execution_count": null,
376 | "metadata": {
377 | "id": "xFbJGnNfiWls"
378 | },
379 | "outputs": [],
380 | "source": [
381 | "webapp = androidmanagement.enterprises().webApps().get(\n",
382 | " name=web_app_name\n",
383 | ").execute()\n",
384 | "\n",
385 | "prettyprint(webapp)"
386 | ]
387 | },
388 | {
389 | "cell_type": "markdown",
390 | "metadata": {
391 | "id": "YiCyDKO2a1VW"
392 | },
393 | "source": [
394 | "## enterprises.webApps.delete\n",
395 | "\n",
396 | "To delete a web app, call `enterprises.webApps.delete`.\n",
397 | "\n",
398 | "*Note* that deleting the web app does not uninstall it from devices it's installed on."
399 | ]
400 | },
401 | {
402 | "cell_type": "code",
403 | "execution_count": null,
404 | "metadata": {
405 | "id": "kxVAAHsNbA7I"
406 | },
407 | "outputs": [],
408 | "source": [
409 | "androidmanagement.enterprises().webApps().delete(\n",
410 | " name=web_app_name\n",
411 | ").execute()"
412 | ]
413 | },
414 | {
415 | "cell_type": "markdown",
416 | "metadata": {
417 | "id": "J5pZM4ze29l3"
418 | },
419 | "source": [
420 | "# Web apps in the Play store\n",
421 | "\n",
422 | "Web apps are private apps, and are therefore only visible to the IT admins and users of the enterprise for which they are created. After logging into managed Google Play, IT admins can view the full store listing of a web app at `https://play.google.com/work/apps/details?id=\u003cpackagename\u003e`.\n",
423 | "\n",
424 | "For users web apps are just like regular Android apps and can see them in the Work store on their device.\n"
425 | ]
426 | },
427 | {
428 | "cell_type": "code",
429 | "execution_count": null,
430 | "metadata": {
431 | "id": "11MnzWiW3lJW"
432 | },
433 | "outputs": [],
434 | "source": [
435 | "print (\"Admins visit https://play.google.com/work/apps/details?id={}, users https://play.google.com/store/apps/details?id={} to see the Play store listing.\".format(web_app_package_name, web_app_package_name))"
436 | ]
437 | }
438 | ],
439 | "metadata": {
440 | "colab": {
441 | "collapsed_sections": [],
442 | "name": "web_apps.ipynb",
443 | "private_outputs": true,
444 | "provenance": [],
445 | "toc_visible": true
446 | },
447 | "kernelspec": {
448 | "display_name": "Python 3",
449 | "name": "python3"
450 | }
451 | },
452 | "nbformat": 4,
453 | "nbformat_minor": 0
454 | }
455 |
--------------------------------------------------------------------------------
/www/oauth_callback.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | OAuth callback
6 |
11 |
24 |
25 |
26 |
Copy this OAuth authentication token and paste it in the Colab notebook: