Skip to content

Commit 458c8fd

Browse files
committed
Feat(Core): handle EMM maanged configurations
1 parent 1198e2b commit 458c8fd

File tree

10 files changed

+310
-4
lines changed

10 files changed

+310
-4
lines changed

.github/workflows/continuous-integration.yml

+1
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ jobs:
6565
##########################################
6666
# Upload APK for Java version
6767
- name: Upload APK Debug for Java
68+
if: always()
6869
uses: actions/upload-artifact@v4
6970
with:
7071
name: Java-Debug-APK

README.md

+25-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ The Inventory Agent for Android allows you to collect a complete inventory of yo
4646

4747
Now you can choose (from the server information) whether this inventory should create a ```Phone``` or a ```Computer``` on GLPI
4848

49-
5049
## Compatibility Matrix
5150

5251
### GLPI Android Inventory Agent
@@ -66,6 +65,31 @@ GLPI Android Inventory Agent is compatible with Android 4.1 and higher (to Andro
6665

6766
Are you having trouble installing our GLPI Android Agent? You can subscribe to our professional support GLPI Network [here](https://services.glpi-network.com).
6867

68+
## Configuring the Agent with an EMM / MDM Tool
69+
70+
The GLPI agent can be deployed/configured from an **MDM** / **EMM** tool
71+
72+
- Samsung Knox
73+
- AirWatch
74+
- InTunes
75+
- MobileIron
76+
- etc.
77+
78+
As long as the **MDM** / **EMM** tool is compatible with [managed configurations](https://developer.android.com/work/managed-configurations), you can configure the GLPI Agent (at deployment or on the fly).
79+
80+
Here is the list of configurable settings:
81+
82+
- **`auto_start_on_boot`** => Run an inventory at startup (`Bool` `true` / `false`)
83+
- **`automatic_inventory`** => Enable automatic inventory (`Bool` `true` / `false`)
84+
- **`frequency`** => Frequency of automatic inventory (`String` `Day` / `Week` / `Month` default `Day`)
85+
- **`server_configuration_list`** => (`Bundle`)
86+
- **`server_url`** => GLPI server URL (`String`)
87+
- **`server_tag`** => TAG (`String`)
88+
- **`server_login`** => Username for basic authentication (`String`)
89+
- **`server_password`** => Password for basic authentication (`String`)
90+
- **`server_itemtype`** => Asset type in GLPI (`String` `Computer` / `Phone` default `Computer`)
91+
- **`server_custom_asset_serial`** => Custom serial number to replace the one generated by the agent (`String`)
92+
6993
## Documentation
7094

7195
We maintain a detailed documentation of the project on the website, check the [How-tos](http://glpi-project.github.io/android-inventory-agent/howtos/) and [Development](http://glpi-project.github.io/android-inventory-agent/) section.

app/build.gradle

+6-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ android {
77

88
defaultConfig {
99
applicationId "org.glpi.inventory.agent"
10-
minSdkVersion 19
10+
minSdkVersion 21
1111
targetSdkVersion 33
1212
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
1313
}
@@ -53,11 +53,16 @@ dependencies {
5353
androidTestImplementation 'androidx.test:rules:1.5.0'
5454
androidTestImplementation 'androidx.test:runner:1.5.2'
5555

56+
/* app restriction */
57+
implementation 'androidx.enterprise:enterprise-feedback:1.0.0'
58+
5659
androidTestImplementation('androidx.test.espresso:espresso-core:3.1.0', {
5760
exclude group: 'com.androidx', module: 'support-annotations'
5861
})
5962
androidTestImplementation 'tools.fastlane:screengrab:2.1.1'
6063

64+
androidTestImplementation 'tools.fastlane:screengrab:2.1.1'
65+
6166
testImplementation 'org.mockito:mockito-core:2.18.3'
6267
androidTestImplementation 'org.mockito:mockito-android:2.18.3'
6368

app/src/main/AndroidManifest.xml

+2
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ android:versionName="">
3939

4040
<meta-data android:name="firebase_crashlytics_collection_enabled" android:value="${crashlyticsEnabled}"/>
4141

42+
<meta-data android:name="android.content.APP_RESTRICTIONS" android:resource="@xml/app_restrictions"/>
43+
4244
<activity android:exported="true" android:name="org.glpi.inventory.agent.ui.ActivitySplash" android:theme="@style/NoActionBar">
4345
<intent-filter>
4446
<action android:name="android.intent.action.MAIN"/>

app/src/main/assets/inventory.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -2712,4 +2712,4 @@
27122712
<DRIVER>wp</DRIVER>
27132713
</CONTROLLERS>
27142714
</CONTENT>
2715-
</REQUEST>
2715+
</REQUEST>

app/src/main/java/org/glpi/inventory/agent/broadcast/TimeAlarm.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,12 @@
4141
import android.content.Context;
4242
import android.content.Intent;
4343
import android.content.SharedPreferences;
44+
import android.os.Build;
4445
import android.os.PowerManager;
4546
import android.preference.PreferenceManager;
4647

48+
import androidx.annotation.RequiresApi;
49+
4750
import org.flyve.inventory.InventoryTask;
4851
import org.glpi.inventory.agent.R;
4952
import org.glpi.inventory.agent.schema.ServerSchema;
@@ -126,14 +129,15 @@ public void onTaskError(Throwable error) {
126129
* Schedules the alarm
127130
* @param context
128131
*/
132+
@RequiresApi(api = Build.VERSION_CODES.M)
129133
public void setAlarm(Context context) {
130134

131135
AgentLog.d("Set Alarm");
132136

133137
AlarmManager am =(AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
134138
Intent i = new Intent(context, TimeAlarm.class);
135139
i.setAction("org.glpi.inventory.agent.ALARM");
136-
PendingIntent pi = PendingIntent.getBroadcast(context, 0, i, 0);
140+
PendingIntent pi = PendingIntent.getBroadcast(context, 0, i, PendingIntent.FLAG_IMMUTABLE);
137141

138142
SharedPreferences customSharedPreference = PreferenceManager.getDefaultSharedPreferences(context);
139143
String timeInventory = customSharedPreference.getString("timeInventory", "Week");

app/src/main/java/org/glpi/inventory/agent/ui/ActivityMain.java

+156
Original file line numberDiff line numberDiff line change
@@ -40,18 +40,22 @@
4040
import android.content.Context;
4141
import android.content.Intent;
4242
import android.content.IntentFilter;
43+
import android.content.RestrictionsManager;
4344
import android.content.SharedPreferences;
4445
import android.content.pm.PackageManager;
4546
import android.os.Build;
4647
import android.os.Bundle;
4748
import android.os.Environment;
49+
import android.os.Parcelable;
4850
import android.preference.PreferenceManager;
4951

5052
import androidx.appcompat.app.ActionBarDrawerToggle;
5153
import androidx.appcompat.app.AppCompatActivity;
5254
import androidx.appcompat.widget.Toolbar;
5355
import androidx.core.app.ActivityCompat;
5456
import androidx.drawerlayout.widget.DrawerLayout;
57+
import androidx.enterprise.feedback.KeyedAppState;
58+
import androidx.enterprise.feedback.KeyedAppStatesReporter;
5559
import androidx.fragment.app.Fragment;
5660
import androidx.fragment.app.FragmentManager;
5761
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
@@ -66,18 +70,27 @@
6670

6771
import com.google.android.material.floatingactionbutton.FloatingActionButton;
6872

73+
import org.flyve.inventory.InventoryTask;
6974
import org.glpi.inventory.agent.R;
75+
import org.glpi.inventory.agent.core.detailserver.DetailServer;
76+
import org.glpi.inventory.agent.core.detailserver.DetailServerPresenter;
7077
import org.glpi.inventory.agent.core.main.Main;
7178
import org.glpi.inventory.agent.core.main.MainPresenter;
7279
import org.glpi.inventory.agent.preference.GlobalParametersPreference;
7380
import org.glpi.inventory.agent.preference.InventoryParametersPreference;
81+
import org.glpi.inventory.agent.schema.ServerSchema;
7482
import org.glpi.inventory.agent.service.InventoryService;
83+
import org.glpi.inventory.agent.utils.AgentLog;
7584
import org.glpi.inventory.agent.utils.Helpers;
85+
import org.glpi.inventory.agent.utils.HttpInventory;
7686
import org.glpi.inventory.agent.utils.LocalPreferences;
7787
import org.glpi.inventory.agent.utils.LocalStorage;
88+
import org.json.JSONException;
89+
import org.json.JSONObject;
7890

7991
import java.util.ArrayList;
8092
import java.util.Collections;
93+
import java.util.List;
8194
import java.util.Map;
8295

8396
public class ActivityMain extends AppCompatActivity
@@ -115,11 +128,150 @@ public void onReceive(Context context, Intent intent) {
115128
}
116129
};
117130

131+
private BroadcastReceiver appRestrictionChange = null;
132+
private KeyedAppStatesReporter appRestrictionChangeReporter = null;
133+
134+
@Override
135+
protected void onStart() {
136+
super.onStart();
137+
IntentFilter restrictionsFilter =
138+
new IntentFilter(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED);
139+
140+
BroadcastReceiver appRestrictionChange = new BroadcastReceiver() {
141+
@Override public void onReceive(Context context, Intent intent) {
142+
resolveRestrictions();
143+
}
144+
};
145+
146+
registerReceiver(appRestrictionChange, restrictionsFilter);
147+
}
148+
149+
@Override
150+
protected void onStop() {
151+
super.onStop();
152+
if (appRestrictionChange != null) {
153+
unregisterReceiver(appRestrictionChange);
154+
appRestrictionChange = null;
155+
}
156+
}
157+
158+
public static void enterpriseFeedback(Context context,
159+
String key,
160+
String message,
161+
String data,
162+
int severity) {
163+
KeyedAppStatesReporter keyedAppStatesReporter = KeyedAppStatesReporter.create(context);
164+
KeyedAppState keyedAppStateMessage = KeyedAppState.builder()
165+
.setSeverity(severity)
166+
.setKey(key)
167+
.setMessage(message)
168+
.setData(data)
169+
.build();
170+
List<KeyedAppState> list = new ArrayList<>();
171+
list.add(keyedAppStateMessage);
172+
keyedAppStatesReporter.setStates(list);
173+
}
174+
175+
private void resolveRestrictions() {
176+
AgentLog.e("EMM - START resolve restrictions");
177+
RestrictionsManager myRestrictionsMgr = null;
178+
myRestrictionsMgr = (RestrictionsManager) getSystemService(Context.RESTRICTIONS_SERVICE);
179+
Bundle appRestrictions = myRestrictionsMgr.getApplicationRestrictions();
180+
181+
SharedPreferences customSharedPreference = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
182+
SharedPreferences.Editor editor = customSharedPreference.edit();
183+
184+
if (appRestrictions.containsKey("automatic_inventory")) {
185+
editor.putBoolean("autoStartInventory", appRestrictions.getBoolean("automatic_inventory"));
186+
enterpriseFeedback(getApplicationContext(), "automatic_inventory", "automatic_inventory option set successfully", appRestrictions.getBoolean("automatic_inventory") ? "true" : "false", KeyedAppState.SEVERITY_INFO);
187+
AgentLog.e("EMM - set automatic inventory to " + appRestrictions.getBoolean("automatic_inventory"));
188+
editor.apply();
189+
}
190+
191+
if (appRestrictions.containsKey("frequency")) {
192+
editor.putString("timeInventory", appRestrictions.getString("frequency"));
193+
enterpriseFeedback(getApplicationContext(), "frequency", "frequency option set successfully", appRestrictions.getString("frequency"), KeyedAppState.SEVERITY_INFO);
194+
AgentLog.e("EMM - set frequency to " + appRestrictions.getString("frequency"));
195+
editor.apply();
196+
}
197+
198+
if (appRestrictions.containsKey("auto_start_on_boot")) {
199+
editor.putBoolean("boot", appRestrictions.getBoolean("auto_start_on_boot"));
200+
enterpriseFeedback(getApplicationContext(), "auto_start_on_boot", "auto_start_on_boot option set successfully", appRestrictions.getBoolean("auto_start_on_boot") ? "true" : "false", KeyedAppState.SEVERITY_INFO);
201+
AgentLog.e("EMM - set auto start on boot to " + appRestrictions.getBoolean("auto_start_on_boot"));
202+
editor.apply();
203+
}
204+
205+
Parcelable[] parcelables = appRestrictions.getParcelableArray("server_configuration_list");
206+
if (parcelables != null && parcelables.length > 0) {
207+
final Context context = getApplicationContext();
208+
for (int i = 0; i < parcelables.length; i++) {
209+
Bundle serverConfig = (Bundle) parcelables[i];
210+
JSONObject jsonServerConfig = new JSONObject();
211+
LocalPreferences preferences = new LocalPreferences(context);
212+
213+
if (serverConfig.getString("server_url").isEmpty()) {
214+
enterpriseFeedback(getApplicationContext(), "server_url", "Error server URL is mandatory -> ", serverConfig.getString("server_url"), KeyedAppState.SEVERITY_ERROR);
215+
AgentLog.e("EMM - server url is mandatory");
216+
continue;
217+
}
218+
219+
try {
220+
jsonServerConfig.put("address", serverConfig.getString("server_url"));
221+
jsonServerConfig.put("tag", serverConfig.getString("server_tag"));
222+
jsonServerConfig.put("login", serverConfig.getString("server_login"));
223+
jsonServerConfig.put("pass", serverConfig.getString("server_password"));
224+
jsonServerConfig.put("itemtype", serverConfig.getString("server_itemtype"));
225+
jsonServerConfig.put("serial", serverConfig.getString("server_custom_asset_serial"));
226+
227+
AgentLog.e("EMM - Receive the following configuration '" + jsonServerConfig.toString());
228+
229+
JSONObject local_server = preferences.loadJSONObject(serverConfig.getString("server_url"));
230+
AgentLog.e("EMM - Try to load '" + serverConfig.getString("server_url") + "' server if exist");
231+
AgentLog.e("EMM - Found '" + local_server.toString() + "'");
232+
AgentLog.e("EMM - Exist ? -> '" + !local_server.toString().equals("{}") + "'");
233+
234+
if (local_server.toString().equals("{}")) {
235+
ArrayList<String> serverArray = preferences.loadServer();
236+
serverArray.add(serverConfig.getString("server_url"));
237+
preferences.saveServer(serverArray);
238+
preferences.saveJSONObject(serverConfig.getString("server_url"), jsonServerConfig);
239+
enterpriseFeedback(getApplicationContext(), "server_url", "server_url added successfully", serverConfig.getString("server_url"), KeyedAppState.SEVERITY_INFO);
240+
enterpriseFeedback(getApplicationContext(), "server_tag", "server_tag added successfully", serverConfig.getString("server_tag"), KeyedAppState.SEVERITY_INFO);
241+
enterpriseFeedback(getApplicationContext(), "server_login", "server_login added successfully", serverConfig.getString("server_login"), KeyedAppState.SEVERITY_INFO);
242+
enterpriseFeedback(getApplicationContext(), "server_password", "server_password added successfully", "***", KeyedAppState.SEVERITY_INFO);
243+
enterpriseFeedback(getApplicationContext(), "server_itemtype", "server_itemtype added successfully", serverConfig.getString("server_itemtype"), KeyedAppState.SEVERITY_INFO);
244+
enterpriseFeedback(getApplicationContext(), "server_custom_asset_serial", "server_custom_asset_serial added successfully", serverConfig.getString("server_custom_asset_serial"), KeyedAppState.SEVERITY_INFO);
245+
AgentLog.e("EMM - Server added successfully");
246+
} else {
247+
preferences.deletePreferences(serverConfig.getString("server_url"));
248+
preferences.saveJSONObject(serverConfig.getString("server_url"), jsonServerConfig);
249+
enterpriseFeedback(getApplicationContext(), "server_url", "server_url updated successfully", serverConfig.getString("server_url"), KeyedAppState.SEVERITY_INFO);
250+
enterpriseFeedback(getApplicationContext(), "server_tag", "server_tag updated successfully", serverConfig.getString("server_tag"), KeyedAppState.SEVERITY_INFO);
251+
enterpriseFeedback(getApplicationContext(), "server_login", "server_login updated successfully", serverConfig.getString("server_login"), KeyedAppState.SEVERITY_INFO);
252+
enterpriseFeedback(getApplicationContext(), "server_password", "server_password updated successfully", "***", KeyedAppState.SEVERITY_INFO);
253+
enterpriseFeedback(getApplicationContext(), "server_itemtype", "server_itemtype updated successfully", serverConfig.getString("server_itemtype"), KeyedAppState.SEVERITY_INFO);
254+
enterpriseFeedback(getApplicationContext(), "server_custom_asset_serial", "server_custom_asset_serial updated successfully", serverConfig.getString("server_custom_asset_serial"), KeyedAppState.SEVERITY_INFO);
255+
AgentLog.e("EMM - Server updated successfully");
256+
}
257+
258+
} catch (JSONException e) {
259+
enterpriseFeedback(getApplicationContext(), "server_url", "error while adding/updating server -> " + e.getMessage(), serverConfig.getString("server_url"), KeyedAppState.SEVERITY_ERROR);
260+
AgentLog.e("EMM - error while adding/updating server");
261+
AgentLog.e("EMM - " + e.getMessage());
262+
}
263+
}
264+
} else {
265+
AgentLog.e("EMM - 'server_configuration_list' key is empty");
266+
}
267+
AgentLog.e("EMM - END resolve restrictions");
268+
}
118269

119270
@Override
120271
protected void onResume() {
121272
super.onResume();
122273
registerReceiver(broadcastReceiver,new IntentFilter(InventoryService.TIMER_RECEIVER));
274+
resolveRestrictions();
123275
}
124276

125277
@Override
@@ -235,6 +387,10 @@ public void onClick(View view) {
235387
}
236388
});
237389

390+
//app restriction change
391+
KeyedAppStatesReporter appRestrictionChangeReporter = KeyedAppStatesReporter.create(getApplicationContext());
392+
393+
238394
}
239395

240396
private void disableFab(){
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<resources>
3+
4+
<!-- Choice restriction for itemtype -->
5+
<string name="title_server_itemtype">Type</string>
6+
<string name="description_server_itemtype">Type of asset</string>
7+
<string name="entry_server_itemtype_computer">Computer</string>
8+
<string name="entry_server_itemtype_phone">Phone</string>
9+
<string-array name="entries_server_itemtype">
10+
<item>@string/entry_server_itemtype_computer</item>
11+
<item>@string/entry_server_itemtype_phone</item>
12+
</string-array>
13+
<string-array name="entry_values_server_itemtype">
14+
<item>Computer</item>
15+
<item>Phone</item>
16+
</string-array>
17+
<string name="default_server_itemtype">Computer</string>
18+
19+
<!-- Choice restriction for frequency -->
20+
<string name="frequency">Frequency</string>
21+
<string name="frequency_description">Frequency</string>
22+
<string name="frequency_day">Day</string>
23+
<string name="frequency_week">Week</string>
24+
<string name="frequency_month">Month</string>
25+
<string-array name="entries_frequency">
26+
<item>@string/frequency_day</item>
27+
<item>@string/frequency_week</item>
28+
<item>@string/frequency_month</item>
29+
</string-array>
30+
<string-array name="entry_values_frequency">
31+
<item>Day</item>
32+
<item>Week</item>
33+
<item>Month</item>
34+
</string-array>
35+
<string name="default_frequency">Day</string>
36+
37+
38+
</resources>

app/src/main/res/values/strings.xml

+12
Original file line numberDiff line numberDiff line change
@@ -302,5 +302,17 @@
302302
<string name="app_is_running_extend">To perform schedule inventory app is running in background</string>
303303
<string name="disable_notification">Disable notification</string>
304304

305+
<!-- message on app restrictions -->
306+
307+
<string name="auto_start_on_boot">Startup from system boot?</string>
308+
<string name="automatic_inventory">Automatic inventory?</string>
309+
<string name="server_list">Server list</string>
310+
<string name="server_configuration">Server configuration</string>
311+
<string name="server_url">Server URL</string>
312+
<string name="server_tag">Server TAG</string>
313+
<string name="server_login">Server login</string>
314+
<string name="server_password">Server password</string>
315+
<string name="server_custom_asset_serial">Custom asset serial</string>
316+
<string name="server_itemtype">Asset itemtype</string>
305317

306318
</resources>

0 commit comments

Comments
 (0)