1d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank/* Copyright (C) 2010 The Android Open Source Project. 2d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank * 3d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank * Licensed under the Apache License, Version 2.0 (the "License"); 4d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank * you may not use this file except in compliance with the License. 5d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank * You may obtain a copy of the License at 6d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank * 7d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank * http://www.apache.org/licenses/LICENSE-2.0 8d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank * 9d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank * Unless required by applicable law or agreed to in writing, software 10d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank * distributed under the License is distributed on an "AS IS" BASIS, 11d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank * See the License for the specific language governing permissions and 13d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank * limitations under the License. 14d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank */ 15d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank 16d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankpackage com.android.exchange.adapter; 17d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank 18d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport android.app.admin.DevicePolicyManager; 19d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport android.content.Context; 20d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport android.content.res.Resources; 21d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport android.os.storage.StorageManager; 22d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport android.os.storage.StorageVolume; 23d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank 24d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport com.android.emailcommon.provider.Policy; 25d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport com.android.exchange.EasSyncService; 26d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport com.android.exchange.R; 27d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank 28d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport org.xmlpull.v1.XmlPullParser; 29d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport org.xmlpull.v1.XmlPullParserException; 30d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport org.xmlpull.v1.XmlPullParserFactory; 31d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank 32d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport java.io.ByteArrayInputStream; 33d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport java.io.IOException; 34d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport java.io.InputStream; 35d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport java.util.ArrayList; 36d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank 37d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank/** 38d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank * Parse the result of the Provision command 39d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank */ 40d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankpublic class ProvisionParser extends Parser { 41d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank private final EasSyncService mService; 42d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank private Policy mPolicy = null; 43d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank private String mSecuritySyncKey = null; 44d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank private boolean mRemoteWipe = false; 45d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank private boolean mIsSupportable = true; 46d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank private boolean smimeRequired = false; 47d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank private final Resources mResources; 48d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank 49d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank public ProvisionParser(InputStream in, EasSyncService service) throws IOException { 50d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank super(in); 51d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank mService = service; 52d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank mResources = service.mContext.getResources(); 53d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 54d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank 55d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank public Policy getPolicy() { 56d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank return mPolicy; 57d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 58d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank 59d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank public String getSecuritySyncKey() { 60d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank return mSecuritySyncKey; 61d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 62d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank 63d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank public void setSecuritySyncKey(String securitySyncKey) { 64d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank mSecuritySyncKey = securitySyncKey; 65d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 66d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank 67d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank public boolean getRemoteWipe() { 68d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank return mRemoteWipe; 69d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 70d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank 71d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank public boolean hasSupportablePolicySet() { 72d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank return (mPolicy != null) && mIsSupportable; 73d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 74d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank 75d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank public void clearUnsupportablePolicies() { 76d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank mIsSupportable = true; 77d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank mPolicy.mProtocolPoliciesUnsupported = null; 78d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 79d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank 80d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank private void addPolicyString(StringBuilder sb, int res) { 81d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank sb.append(mResources.getString(res)); 82d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank sb.append(Policy.POLICY_STRING_DELIMITER); 83d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 84d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank 85d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank /** 86d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank * Complete setup of a Policy; we normalize it first (removing inconsistencies, etc.) and then 87d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank * generate the tokenized "protocol policies enforced" string. Note that unsupported policies 88d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank * must have been added prior to calling this method (this is only a possibility with wbxml 89d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank * policy documents, as all versions of the OS support the policies in xml documents). 90d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank */ 91d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank private void setPolicy(Policy policy) { 92d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank policy.normalize(); 93d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank StringBuilder sb = new StringBuilder(); 94d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank if (policy.mDontAllowAttachments) { 95d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank addPolicyString(sb, R.string.policy_dont_allow_attachments); 96d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 97d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank if (policy.mRequireManualSyncWhenRoaming) { 98d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank addPolicyString(sb, R.string.policy_require_manual_sync_roaming); 99d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 100d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank policy.mProtocolPoliciesEnforced = sb.toString(); 101d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank mPolicy = policy; 102d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 103d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank 104d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank private boolean deviceSupportsEncryption() { 105d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank DevicePolicyManager dpm = (DevicePolicyManager) 106d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank mService.mContext.getSystemService(Context.DEVICE_POLICY_SERVICE); 107d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank int status = dpm.getStorageEncryptionStatus(); 108d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank return status != DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED; 109d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 110d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank 111d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank private void parseProvisionDocWbxml() throws IOException { 112d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank Policy policy = new Policy(); 113d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank ArrayList<Integer> unsupportedList = new ArrayList<Integer>(); 114d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank boolean passwordEnabled = false; 115d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank 116d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank while (nextTag(Tags.PROVISION_EAS_PROVISION_DOC) != END) { 117d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank boolean tagIsSupported = true; 118d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank int res = 0; 119d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank switch (tag) { 120d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_DEVICE_PASSWORD_ENABLED: 121d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank if (getValueInt() == 1) { 122d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank passwordEnabled = true; 123d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank if (policy.mPasswordMode == Policy.PASSWORD_MODE_NONE) { 124d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank policy.mPasswordMode = Policy.PASSWORD_MODE_SIMPLE; 125d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 126d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 127d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank break; 128d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_MIN_DEVICE_PASSWORD_LENGTH: 129d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank policy.mPasswordMinLength = getValueInt(); 130d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank break; 131d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_ALPHA_DEVICE_PASSWORD_ENABLED: 132d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank if (getValueInt() == 1) { 133d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank policy.mPasswordMode = Policy.PASSWORD_MODE_STRONG; 134d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 135d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank break; 136d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_MAX_INACTIVITY_TIME_DEVICE_LOCK: 137d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank // EAS gives us seconds, which is, happily, what the PolicySet requires 138d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank policy.mMaxScreenLockTime = getValueInt(); 139d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank break; 140d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_MAX_DEVICE_PASSWORD_FAILED_ATTEMPTS: 141d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank policy.mPasswordMaxFails = getValueInt(); 142d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank break; 143d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_DEVICE_PASSWORD_EXPIRATION: 144d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank policy.mPasswordExpirationDays = getValueInt(); 145d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank break; 146d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_DEVICE_PASSWORD_HISTORY: 147d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank policy.mPasswordHistory = getValueInt(); 148d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank break; 149d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_ALLOW_CAMERA: 150d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank policy.mDontAllowCamera = (getValueInt() == 0); 151d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank break; 152d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_ALLOW_SIMPLE_DEVICE_PASSWORD: 153d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank // Ignore this unless there's any MSFT documentation for what this means 154d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank // Hint: I haven't seen any that's more specific than "simple" 155d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank getValue(); 156d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank break; 157d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank // The following policies, if false, can't be supported at the moment 158d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_ALLOW_STORAGE_CARD: 159d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_ALLOW_UNSIGNED_APPLICATIONS: 160d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_ALLOW_UNSIGNED_INSTALLATION_PACKAGES: 161d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_ALLOW_WIFI: 162d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_ALLOW_TEXT_MESSAGING: 163d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_ALLOW_POP_IMAP_EMAIL: 164d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_ALLOW_IRDA: 165d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_ALLOW_HTML_EMAIL: 166d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_ALLOW_BROWSER: 167d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_ALLOW_CONSUMER_EMAIL: 168d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_ALLOW_INTERNET_SHARING: 169d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank if (getValueInt() == 0) { 170d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank tagIsSupported = false; 171d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank switch(tag) { 172d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_ALLOW_STORAGE_CARD: 173d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank res = R.string.policy_dont_allow_storage_cards; 174d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank break; 175d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_ALLOW_UNSIGNED_APPLICATIONS: 176d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank res = R.string.policy_dont_allow_unsigned_apps; 177d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank break; 178d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_ALLOW_UNSIGNED_INSTALLATION_PACKAGES: 179d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank res = R.string.policy_dont_allow_unsigned_installers; 180d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank break; 181d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_ALLOW_WIFI: 182d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank res = R.string.policy_dont_allow_wifi; 183d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank break; 184d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_ALLOW_TEXT_MESSAGING: 185d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank res = R.string.policy_dont_allow_text_messaging; 186d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank break; 187d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_ALLOW_POP_IMAP_EMAIL: 188d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank res = R.string.policy_dont_allow_pop_imap; 189d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank break; 190d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_ALLOW_IRDA: 191d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank res = R.string.policy_dont_allow_irda; 192d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank break; 193d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_ALLOW_HTML_EMAIL: 194d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank res = R.string.policy_dont_allow_html; 195d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank policy.mDontAllowHtml = true; 196d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank break; 197d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_ALLOW_BROWSER: 198d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank res = R.string.policy_dont_allow_browser; 199d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank break; 200d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_ALLOW_CONSUMER_EMAIL: 201d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank res = R.string.policy_dont_allow_consumer_email; 202d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank break; 203d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_ALLOW_INTERNET_SHARING: 204d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank res = R.string.policy_dont_allow_internet_sharing; 205d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank break; 206d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 207d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank if (res > 0) { 208d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank unsupportedList.add(res); 209d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 210d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 211d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank break; 212d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_ATTACHMENTS_ENABLED: 213d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank policy.mDontAllowAttachments = getValueInt() != 1; 214d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank break; 215d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank // Bluetooth: 0 = no bluetooth; 1 = only hands-free; 2 = allowed 216d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_ALLOW_BLUETOOTH: 217d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank if (getValueInt() != 2) { 218d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank tagIsSupported = false; 219d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank unsupportedList.add(R.string.policy_bluetooth_restricted); 220d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 221d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank break; 222d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank // We may now support device (internal) encryption; we'll check this capability 223d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank // below with the call to SecurityPolicy.isSupported() 224d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_REQUIRE_DEVICE_ENCRYPTION: 225d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank if (getValueInt() == 1) { 226d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank if (!deviceSupportsEncryption()) { 227d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank tagIsSupported = false; 228d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank unsupportedList.add(R.string.policy_require_encryption); 229d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } else { 230d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank policy.mRequireEncryption = true; 231d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 232d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 233d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank break; 234d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank // Note that DEVICE_ENCRYPTION_ENABLED refers to SD card encryption, which the OS 235d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank // does not yet support. 236d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_DEVICE_ENCRYPTION_ENABLED: 237d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank if (getValueInt() == 1) { 238d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank log("Policy requires SD card encryption"); 239d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank // Let's see if this can be supported on our device... 240d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank if (deviceSupportsEncryption()) { 241d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank StorageManager sm = (StorageManager)mService.mContext.getSystemService( 242d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank Context.STORAGE_SERVICE); 243d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank // NOTE: Private API! 244d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank // Go through volumes; if ANY are removable, we can't support this 245d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank // policy. 246d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank StorageVolume[] volumeList = sm.getVolumeList(); 247d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank for (StorageVolume volume: volumeList) { 248d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank if (volume.isRemovable()) { 249d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank tagIsSupported = false; 250cebad501386e803eaea73f6c4301e370a4d4e222Fabrice Di Meglio log("Removable: " + volume.getDescription(mService.mContext)); 251d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank break; // Break only from the storage volume loop 252d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } else { 253cebad501386e803eaea73f6c4301e370a4d4e222Fabrice Di Meglio log("Not Removable: " + volume.getDescription(mService.mContext)); 254d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 255d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 256d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank if (tagIsSupported) { 257d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank // If this policy is requested, we MUST also require encryption 258d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank log("Device supports SD card encryption"); 259d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank policy.mRequireEncryption = true; 260d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank break; 261d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 262d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } else { 263d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank log("Device doesn't support encryption; failing"); 264d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank tagIsSupported = false; 265d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 266d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank // If we fall through, we can't support the policy 267d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank unsupportedList.add(R.string.policy_require_sd_encryption); 268d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 269d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank break; 270d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank // Note this policy; we enforce it in ExchangeService 271d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_REQUIRE_MANUAL_SYNC_WHEN_ROAMING: 272d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank policy.mRequireManualSyncWhenRoaming = getValueInt() == 1; 273d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank break; 274d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank // We are allowed to accept policies, regardless of value of this tag 275d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank // TODO: When we DO support a recovery password, we need to store the value in 276d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank // the account (so we know to utilize it) 277d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_PASSWORD_RECOVERY_ENABLED: 278d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank // Read, but ignore, value 279d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank policy.mPasswordRecoveryEnabled = getValueInt() == 1; 280d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank break; 281d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank // The following policies, if true, can't be supported at the moment 282d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_REQUIRE_SIGNED_SMIME_MESSAGES: 283d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_REQUIRE_ENCRYPTED_SMIME_MESSAGES: 284d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_REQUIRE_SIGNED_SMIME_ALGORITHM: 285d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_REQUIRE_ENCRYPTION_SMIME_ALGORITHM: 286d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank if (getValueInt() == 1) { 287d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank tagIsSupported = false; 288d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank if (!smimeRequired) { 289d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank unsupportedList.add(R.string.policy_require_smime); 290d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank smimeRequired = true; 291d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 292d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 293d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank break; 294d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_MAX_ATTACHMENT_SIZE: 295d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank int max = getValueInt(); 296d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank if (max > 0) { 297d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank policy.mMaxAttachmentSize = max; 298d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 299d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank break; 300d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank // Complex characters are supported 301d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_MIN_DEVICE_PASSWORD_COMPLEX_CHARS: 302d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank policy.mPasswordComplexChars = getValueInt(); 303d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank break; 304d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank // The following policies are moot; they allow functionality that we don't support 305d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_ALLOW_DESKTOP_SYNC: 306d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_ALLOW_SMIME_ENCRYPTION_NEGOTIATION: 307d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_ALLOW_SMIME_SOFT_CERTS: 308d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_ALLOW_REMOTE_DESKTOP: 309d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank skipTag(); 310d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank break; 311d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank // We don't handle approved/unapproved application lists 312d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_UNAPPROVED_IN_ROM_APPLICATION_LIST: 313d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_APPROVED_APPLICATION_LIST: 314d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank // Parse and throw away the content 315d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank if (specifiesApplications(tag)) { 316d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank tagIsSupported = false; 317d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank if (tag == Tags.PROVISION_UNAPPROVED_IN_ROM_APPLICATION_LIST) { 318d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank unsupportedList.add(R.string.policy_app_blacklist); 319d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } else { 320d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank unsupportedList.add(R.string.policy_app_whitelist); 321d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 322d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 323d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank break; 324d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank // We accept calendar age, since we never ask for more than two weeks, and that's 325d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank // the most restrictive policy 326d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_MAX_CALENDAR_AGE_FILTER: 327d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank policy.mMaxCalendarLookback = getValueInt(); 328d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank break; 329d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank // We handle max email lookback 330d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_MAX_EMAIL_AGE_FILTER: 331d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank policy.mMaxEmailLookback = getValueInt(); 332d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank break; 333d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank // We currently reject these next two policies 334d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_MAX_EMAIL_BODY_TRUNCATION_SIZE: 335d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_MAX_EMAIL_HTML_BODY_TRUNCATION_SIZE: 336d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank String value = getValue(); 337d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank // -1 indicates no required truncation 338d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank if (!value.equals("-1")) { 339d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank max = Integer.parseInt(value); 340d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank if (tag == Tags.PROVISION_MAX_EMAIL_BODY_TRUNCATION_SIZE) { 341d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank policy.mMaxTextTruncationSize = max; 342d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank unsupportedList.add(R.string.policy_text_truncation); 343d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } else { 344d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank policy.mMaxHtmlTruncationSize = max; 345d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank unsupportedList.add(R.string.policy_html_truncation); 346d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 347d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank tagIsSupported = false; 348d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 349d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank break; 350d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank default: 351d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank skipTag(); 352d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 353d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank 354d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank if (!tagIsSupported) { 355d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank log("Policy not supported: " + tag); 356d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank mIsSupportable = false; 357d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 358d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 359d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank 360d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank // Make sure policy settings are valid; password not enabled trumps other password settings 361d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank if (!passwordEnabled) { 362d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank policy.mPasswordMode = Policy.PASSWORD_MODE_NONE; 363d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 364d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank 365d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank if (!unsupportedList.isEmpty()) { 366d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank StringBuilder sb = new StringBuilder(); 367d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank for (int res: unsupportedList) { 368d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank addPolicyString(sb, res); 369d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 370d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank policy.mProtocolPoliciesUnsupported = sb.toString(); 371d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 372d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank 373d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank setPolicy(policy); 374d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 375d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank 376d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank /** 377d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank * Return whether or not either of the application list tags specifies any applications 378d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank * @param endTag the tag whose children we're walking through 379d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank * @return whether any applications were specified (by name or by hash) 380d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank * @throws IOException 381d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank */ 382d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank private boolean specifiesApplications(int endTag) throws IOException { 383d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank boolean specifiesApplications = false; 384d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank while (nextTag(endTag) != END) { 385d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank switch (tag) { 386d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_APPLICATION_NAME: 387d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_HASH: 388d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank specifiesApplications = true; 389d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank break; 390d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank default: 391d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank skipTag(); 392d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 393d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 394d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank return specifiesApplications; 395d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 396d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank 397d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank /*package*/ void parseProvisionDocXml(String doc) throws IOException { 398d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank Policy policy = new Policy(); 399d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank 400d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank try { 401d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); 402d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank XmlPullParser parser = factory.newPullParser(); 403d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank parser.setInput(new ByteArrayInputStream(doc.getBytes()), "UTF-8"); 404d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank int type = parser.getEventType(); 405d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank if (type == XmlPullParser.START_DOCUMENT) { 406d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank type = parser.next(); 407d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank if (type == XmlPullParser.START_TAG) { 408d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank String tagName = parser.getName(); 409d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank if (tagName.equals("wap-provisioningdoc")) { 410d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank parseWapProvisioningDoc(parser, policy); 411d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 412d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 413d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 414d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } catch (XmlPullParserException e) { 415d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank throw new IOException(); 416d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 417d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank 418d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank setPolicy(policy); 419d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 420d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank 421d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank /** 422d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank * Return true if password is required; otherwise false. 423d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank */ 424d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank private boolean parseSecurityPolicy(XmlPullParser parser, Policy policy) 425d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank throws XmlPullParserException, IOException { 426d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank boolean passwordRequired = true; 427d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank while (true) { 428d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank int type = parser.nextTag(); 429d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank if (type == XmlPullParser.END_TAG && parser.getName().equals("characteristic")) { 430d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank break; 431d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } else if (type == XmlPullParser.START_TAG) { 432d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank String tagName = parser.getName(); 433d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank if (tagName.equals("parm")) { 434d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank String name = parser.getAttributeValue(null, "name"); 435d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank if (name.equals("4131")) { 436d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank String value = parser.getAttributeValue(null, "value"); 437d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank if (value.equals("1")) { 438d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank passwordRequired = false; 439d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 440d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 441d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 442d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 443d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 444d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank return passwordRequired; 445d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 446d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank 447d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank private void parseCharacteristic(XmlPullParser parser, Policy policy) 448d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank throws XmlPullParserException, IOException { 449d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank boolean enforceInactivityTimer = true; 450d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank while (true) { 451d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank int type = parser.nextTag(); 452d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank if (type == XmlPullParser.END_TAG && parser.getName().equals("characteristic")) { 453d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank break; 454d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } else if (type == XmlPullParser.START_TAG) { 455d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank if (parser.getName().equals("parm")) { 456d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank String name = parser.getAttributeValue(null, "name"); 457d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank String value = parser.getAttributeValue(null, "value"); 458d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank if (name.equals("AEFrequencyValue")) { 459d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank if (enforceInactivityTimer) { 460d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank if (value.equals("0")) { 461d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank policy.mMaxScreenLockTime = 1; 462d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } else { 463d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank policy.mMaxScreenLockTime = 60*Integer.parseInt(value); 464d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 465d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 466d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } else if (name.equals("AEFrequencyType")) { 467d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank // "0" here means we don't enforce an inactivity timeout 468d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank if (value.equals("0")) { 469d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank enforceInactivityTimer = false; 470d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 471d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } else if (name.equals("DeviceWipeThreshold")) { 472d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank policy.mPasswordMaxFails = Integer.parseInt(value); 473d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } else if (name.equals("CodewordFrequency")) { 474d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank // Ignore; has no meaning for us 475d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } else if (name.equals("MinimumPasswordLength")) { 476d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank policy.mPasswordMinLength = Integer.parseInt(value); 477d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } else if (name.equals("PasswordComplexity")) { 478d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank if (value.equals("0")) { 479d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank policy.mPasswordMode = Policy.PASSWORD_MODE_STRONG; 480d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } else { 481d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank policy.mPasswordMode = Policy.PASSWORD_MODE_SIMPLE; 482d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 483d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 484d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 485d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 486d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 487d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 488d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank 489d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank private void parseRegistry(XmlPullParser parser, Policy policy) 490d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank throws XmlPullParserException, IOException { 491d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank while (true) { 492d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank int type = parser.nextTag(); 493d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank if (type == XmlPullParser.END_TAG && parser.getName().equals("characteristic")) { 494d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank break; 495d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } else if (type == XmlPullParser.START_TAG) { 496d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank String name = parser.getName(); 497d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank if (name.equals("characteristic")) { 498d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank parseCharacteristic(parser, policy); 499d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 500d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 501d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 502d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 503d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank 504d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank private void parseWapProvisioningDoc(XmlPullParser parser, Policy policy) 505d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank throws XmlPullParserException, IOException { 506d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank while (true) { 507d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank int type = parser.nextTag(); 508d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank if (type == XmlPullParser.END_TAG && parser.getName().equals("wap-provisioningdoc")) { 509d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank break; 510d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } else if (type == XmlPullParser.START_TAG) { 511d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank String name = parser.getName(); 512d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank if (name.equals("characteristic")) { 513d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank String atype = parser.getAttributeValue(null, "type"); 514d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank if (atype.equals("SecurityPolicy")) { 515d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank // If a password isn't required, stop here 516d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank if (!parseSecurityPolicy(parser, policy)) { 517d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank return; 518d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 519d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } else if (atype.equals("Registry")) { 520d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank parseRegistry(parser, policy); 521d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank return; 522d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 523d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 524d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 525d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 526d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 527d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank 528d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank private void parseProvisionData() throws IOException { 529d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank while (nextTag(Tags.PROVISION_DATA) != END) { 530d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank if (tag == Tags.PROVISION_EAS_PROVISION_DOC) { 531d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank parseProvisionDocWbxml(); 532d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } else { 533d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank skipTag(); 534d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 535d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 536d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 537d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank 538d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank private void parsePolicy() throws IOException { 539d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank String policyType = null; 540d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank while (nextTag(Tags.PROVISION_POLICY) != END) { 541d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank switch (tag) { 542d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_POLICY_TYPE: 543d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank policyType = getValue(); 544d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank mService.userLog("Policy type: ", policyType); 545d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank break; 546d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_POLICY_KEY: 547d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank mSecuritySyncKey = getValue(); 548d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank break; 549d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_STATUS: 550d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank mService.userLog("Policy status: ", getValue()); 551d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank break; 552d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_DATA: 553d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank if (policyType.equalsIgnoreCase(EasSyncService.EAS_2_POLICY_TYPE)) { 554d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank // Parse the old style XML document 555d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank parseProvisionDocXml(getValue()); 556d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } else { 557d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank // Parse the newer WBXML data 558d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank parseProvisionData(); 559d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 560d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank break; 561d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank default: 562d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank skipTag(); 563d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 564d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 565d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 566d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank 567d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank private void parsePolicies() throws IOException { 568d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank while (nextTag(Tags.PROVISION_POLICIES) != END) { 569d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank if (tag == Tags.PROVISION_POLICY) { 570d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank parsePolicy(); 571d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } else { 572d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank skipTag(); 573d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 574d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 575d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 576d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank 577d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank private void parseDeviceInformation() throws IOException { 578d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank while (nextTag(Tags.SETTINGS_DEVICE_INFORMATION) != END) { 579d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank if (tag == Tags.SETTINGS_STATUS) { 580d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank mService.userLog("DeviceInformation status: " + getValue()); 581d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } else { 582d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank skipTag(); 583d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 584d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 585d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 586d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank 587d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank @Override 588d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank public boolean parse() throws IOException { 589d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank boolean res = false; 590d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank if (nextTag(START_DOCUMENT) != Tags.PROVISION_PROVISION) { 591d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank throw new IOException(); 592d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 593d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank while (nextTag(START_DOCUMENT) != END_DOCUMENT) { 594d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank switch (tag) { 595d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_STATUS: 596d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank int status = getValueInt(); 597d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank mService.userLog("Provision status: ", status); 598d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank res = (status == 1); 599d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank break; 600d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.SETTINGS_DEVICE_INFORMATION: 601d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank parseDeviceInformation(); 602d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank break; 603d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_POLICIES: 604d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank parsePolicies(); 605d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank break; 606d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank case Tags.PROVISION_REMOTE_WIPE: 607d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank // Indicate remote wipe command received 608d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank mRemoteWipe = true; 609d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank break; 610d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank default: 611d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank skipTag(); 612d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 613d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 614d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank return res; 615d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank } 616d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank} 617