VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/medium/UIMedium.cpp@ 71006

Last change on this file since 71006 was 71006, checked in by vboxsync, 7 years ago

FE/Qt: bugref:9119: A try to fix a crash while opening New VM wizard and all similar places which uses similar code piece.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 22.6 KB
Line 
1/* $Id: UIMedium.cpp 71006 2018-02-14 11:21:33Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UIMedium class implementation.
4 */
5
6/*
7 * Copyright (C) 2009-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#ifdef VBOX_WITH_PRECOMPILED_HEADERS
19# include <precomp.h>
20#else /* !VBOX_WITH_PRECOMPILED_HEADERS */
21/* Qt includes: */
22# include <QDir>
23/* GUI includes: */
24# include "UIMedium.h"
25# include "VBoxGlobal.h"
26# include "UIConverter.h"
27# include "UIErrorString.h"
28# include "UIExtraDataManager.h"
29# include "UIIconPool.h"
30/* COM includes: */
31# include "CMachine.h"
32# include "CSnapshot.h"
33#endif /* !VBOX_WITH_PRECOMPILED_HEADERS */
34
35QString UIMedium::m_sstrNullID = QUuid().toString().remove('{').remove('}');
36QString UIMedium::m_sstrTable = QString("<table>%1</table>");
37QString UIMedium::m_sstrRow = QString("<tr><td>%1</td></tr>");
38
39UIMedium::UIMedium()
40 : m_type(UIMediumType_Invalid)
41 , m_medium(CMedium())
42 , m_state(KMediumState_NotCreated)
43 , m_enmMediumType(KMediumType_Max)
44 , m_enmMediumVariant(KMediumVariant_Max)
45{
46 refresh();
47}
48
49UIMedium::UIMedium(const CMedium &medium, UIMediumType type)
50 : m_type(type)
51 , m_medium(medium)
52 , m_state(KMediumState_NotCreated)
53 , m_enmMediumType(KMediumType_Max)
54 , m_enmMediumVariant(KMediumVariant_Max)
55{
56 refresh();
57}
58
59UIMedium::UIMedium(const CMedium &medium, UIMediumType type, KMediumState state)
60 : m_type(type)
61 , m_medium(medium)
62 , m_state(state)
63 , m_enmMediumType(KMediumType_Max)
64 , m_enmMediumVariant(KMediumVariant_Max)
65{
66 refresh();
67}
68
69UIMedium::UIMedium(const UIMedium &other)
70{
71 *this = other;
72}
73
74UIMedium& UIMedium::operator=(const UIMedium &other)
75{
76 m_type = other.type();
77
78 m_medium = other.medium();
79
80 m_state = other.state();
81 m_result = other.result();
82 m_strLastAccessError = other.lastAccessError();
83
84 m_strId = other.id();
85 m_strRootId = other.rootID();
86 m_strParentId = other.parentID();
87
88 m_strKey = other.key();
89
90 m_strName = other.name();
91 m_strLocation = other.location();
92 m_strDescription = other.description();
93
94 m_uSize = other.sizeInBytes();
95 m_uLogicalSize = other.logicalSizeInBytes();
96 m_strSize = other.size();
97 m_strLogicalSize = other.logicalSize();
98
99 m_enmMediumType = other.mediumType();
100 m_enmMediumVariant = other.mediumVariant();
101
102 m_strHardDiskType = other.hardDiskType();
103 m_strHardDiskFormat = other.hardDiskFormat();
104 m_fHasChildren = other.hasChildren();
105 m_strStorageDetails = other.storageDetails();
106 m_strEncryptionPasswordID = other.encryptionPasswordID();
107
108 m_strUsage = other.usage();
109 m_strToolTip = other.tip();
110 m_machineIds = other.machineIds();
111 m_curStateMachineIds = other.curStateMachineIds();
112
113 m_noDiffs = other.cache();
114
115 m_fHidden = other.m_fHidden;
116 m_fUsedByHiddenMachinesOnly = other.m_fUsedByHiddenMachinesOnly;
117 m_fReadOnly = other.isReadOnly();
118 m_fUsedInSnapshots = other.isUsedInSnapshots();
119 m_fHostDrive = other.isHostDrive();
120 m_fEncrypted = other.isEncrypted();
121
122 return *this;
123}
124
125void UIMedium::blockAndQueryState()
126{
127 /* Ignore for NULL medium: */
128 if (m_medium.isNull())
129 return;
130
131 /* Acquire actual medium state: */
132 m_state = m_medium.RefreshState();
133
134 /* Save the result to distinguish between
135 * inaccessible and e.g. uninitialized objects: */
136 m_result = COMResult(m_medium);
137 if (!m_result.isOk())
138 {
139 m_state = KMediumState_Inaccessible;
140 m_strLastAccessError = QString();
141 }
142 else
143 m_strLastAccessError = m_medium.GetLastAccessError();
144
145 /* Refresh finally: */
146 refresh();
147}
148
149void UIMedium::refresh()
150{
151 /* Reset ID parameters: */
152 m_strId = nullID();
153 m_strRootId = nullID();
154 m_strParentId = nullID();
155
156 /* Reset cache parameters: */
157 //m_strKey = nullID();
158
159 /* Reset name/location/description/size parameters: */
160 m_strName = VBoxGlobal::tr("Empty", "medium");
161 m_strLocation = m_strSize = m_strLogicalSize = QString("--");
162 m_strDescription = QString();
163 m_uSize = m_uLogicalSize = 0;
164
165 /* Reset medium type & variant parameter: */
166 m_enmMediumType = KMediumType_Max;
167 m_enmMediumVariant = KMediumVariant_Max;
168
169 /* Reset hard drive related parameters: */
170 m_strHardDiskType = QString();
171 m_strHardDiskFormat = QString();
172 m_fHasChildren = false;
173 m_strStorageDetails = QString();
174 m_strEncryptionPasswordID = QString();
175
176 /* Reset data parameters: */
177 m_strUsage = QString();
178 m_strToolTip = QString();
179 m_machineIds.clear();
180 m_curStateMachineIds.clear();
181
182 /* Reset m_noDiffs: */
183 m_noDiffs.isSet = false;
184
185 /* Reset flags: */
186 m_fHidden = false;
187 m_fUsedByHiddenMachinesOnly = false;
188 m_fReadOnly = false;
189 m_fUsedInSnapshots = false;
190 m_fHostDrive = false;
191 m_fEncrypted = false;
192
193 /* For non NULL medium: */
194 if (!m_medium.isNull())
195 {
196 /* Refresh medium ID: */
197 m_strId = normalizedID(m_medium.GetId());
198 /* Refresh root medium ID: */
199 m_strRootId = m_strId;
200
201 /* Init medium key if necessary: */
202 if (m_strKey.isNull())
203 m_strKey = m_strId;
204
205 /* Check whether this is host-drive medium: */
206 m_fHostDrive = m_medium.GetHostDrive();
207
208 /* Refresh medium description: */
209 m_strDescription = m_medium.GetDescription();
210
211 /* Refresh medium name: */
212 if (!m_fHostDrive)
213 m_strName = m_medium.GetName();
214 else if (m_strDescription.isEmpty())
215 m_strName = VBoxGlobal::tr("Host Drive '%1'", "medium").arg(QDir::toNativeSeparators(m_medium.GetLocation()));
216 else
217 m_strName = VBoxGlobal::tr("Host Drive %1 (%2)", "medium").arg(m_strDescription, m_medium.GetName());
218 /* Refresh medium location: */
219 if (!m_fHostDrive)
220 m_strLocation = QDir::toNativeSeparators(m_medium.GetLocation());
221
222 /* Refresh medium size and logical size: */
223 if (!m_fHostDrive)
224 {
225 /* Only for created and accessible mediums: */
226 if (m_state != KMediumState_Inaccessible && m_state != KMediumState_NotCreated)
227 {
228 m_uSize = m_medium.GetSize();
229 m_strSize = vboxGlobal().formatSize(m_uSize);
230 if (m_type == UIMediumType_HardDisk)
231 {
232 m_uLogicalSize = m_medium.GetLogicalSize();
233 m_strLogicalSize = vboxGlobal().formatSize(m_uLogicalSize);
234 }
235 else
236 {
237 m_uLogicalSize = m_uSize;
238 m_strLogicalSize = m_strSize;
239 }
240 }
241 }
242
243 /* Refresh medium type & variant: */
244 m_enmMediumType = m_medium.GetType();
245 qlonglong iMediumVariant = 0;
246 foreach (const KMediumVariant &enmVariant, m_medium.GetVariant())
247 iMediumVariant |= enmVariant;
248 m_enmMediumVariant = (KMediumVariant)iMediumVariant;
249
250 /* For hard drive medium: */
251 if (m_type == UIMediumType_HardDisk)
252 {
253 /* Refresh hard drive disk type: */
254 m_strHardDiskType = mediumTypeToString(m_medium);
255 /* Refresh hard drive format: */
256 m_strHardDiskFormat = m_medium.GetFormat();
257
258 /* Refresh hard drive parental status: */
259 m_fHasChildren = m_medium.GetChildren().size();
260
261 /* Refresh hard drive storage details: */
262 m_strStorageDetails = gpConverter->toString(m_enmMediumVariant);
263
264 /* Check whether this is read-only hard drive: */
265 m_fReadOnly = m_medium.GetReadOnly();
266
267 /* Refresh parent hard drive ID: */
268 CMedium parentMedium = m_medium.GetParent();
269 if (!parentMedium.isNull())
270 m_strParentId = normalizedID(parentMedium.GetId());
271
272 /* Only for created and accessible mediums: */
273 if (m_state != KMediumState_Inaccessible && m_state != KMediumState_NotCreated)
274 {
275 /* Refresh root hard drive ID: */
276 while (!parentMedium.isNull())
277 {
278 m_strRootId = normalizedID(parentMedium.GetId());
279 parentMedium = parentMedium.GetParent();
280 }
281
282 /* Refresh encryption attributes: */
283 if (m_strRootId != m_strId)
284 {
285 m_strEncryptionPasswordID = root().encryptionPasswordID();
286 m_fEncrypted = root().isEncrypted();
287 }
288 else
289 {
290 QString strCipher;
291 CMedium medium(m_medium);
292 const QString strEncryptionPasswordID = medium.GetEncryptionSettings(strCipher);
293 if (medium.isOk())
294 {
295 m_strEncryptionPasswordID = strEncryptionPasswordID;
296 m_fEncrypted = true;
297 }
298 }
299 }
300 }
301
302 /* Check whether this is hidden medium: */
303 QString strHints = m_medium.GetProperty("Special/GUI/Hints");
304 if (!strHints.isEmpty())
305 {
306 QStringList hints(strHints.split(','));
307 if (hints.contains("Hide", Qt::CaseInsensitive))
308 m_fHidden = true;
309 }
310
311 /* Refresh usage data: */
312 m_curStateMachineIds.clear();
313 m_machineIds = m_medium.GetMachineIds().toList();
314 if (m_machineIds.size() > 0)
315 {
316 /* Get CVirtualBox object: */
317 CVirtualBox vbox = vboxGlobal().virtualBox();
318
319 /* By default we assuming that this medium is attached
320 * to 'hidden' machines only, if at least one machine present: */
321 m_fUsedByHiddenMachinesOnly = true;
322
323 /* Prepare machine usage: */
324 QString strMachineUsage;
325 /* Walk through all the machines this medium attached to: */
326 foreach (const QString &strMachineID, m_machineIds)
327 {
328 /* Look for the corresponding machine: */
329 CMachine machine = vbox.FindMachine(strMachineID);
330
331 /* UIMedium object can wrap newly created CMedium object
332 * which belongs to not yet registered machine, like while creating VM clone.
333 * We can skip such a machines in usage string. */
334 if (machine.isNull())
335 {
336 /* Since we can't precisely check 'hidden' status for that machine in such case,
337 * we have to assume that medium attached not only to 'hidden' machines: */
338 m_fUsedByHiddenMachinesOnly = false;
339 continue;
340 }
341
342 /* Finally we can precisely check if current machine is 'hidden': */
343 if (gEDataManager->showMachineInSelectorChooser(strMachineID))
344 m_fUsedByHiddenMachinesOnly = false;
345
346 /* Prepare snapshot usage: */
347 QString strSnapshotUsage;
348 /* Walk through all the snapshots this medium attached to: */
349 foreach (const QString &strSnapshotID, m_medium.GetSnapshotIds(strMachineID))
350 {
351 if (strSnapshotID == strMachineID)
352 {
353 /* The medium is attached to the machine in the current
354 * state, we don't distinguish this for now by always
355 * giving the VM name in front of snapshot names. */
356 m_curStateMachineIds.push_back(strSnapshotID);
357 continue;
358 }
359
360 /* Look for the corresponding snapshot: */
361 CSnapshot snapshot = machine.FindSnapshot(strSnapshotID);
362
363 /* Snapshot can be NULL while takeSnaphot is in progress: */
364 if (snapshot.isNull())
365 continue;
366
367 /* Refresh snapshot usage flag: */
368 m_fUsedInSnapshots = true;
369
370 /* Append snapshot usage: */
371 if (!strSnapshotUsage.isNull())
372 strSnapshotUsage += ", ";
373 strSnapshotUsage += snapshot.GetName();
374 }
375
376 /* Append machine usage: */
377 if (!strMachineUsage.isNull())
378 strMachineUsage += ", ";
379 strMachineUsage += machine.GetName();
380
381 /* Append snapshot usage: */
382 if (!strSnapshotUsage.isNull())
383 strMachineUsage += QString(" (%2)").arg(strSnapshotUsage);
384 }
385
386 /* Append machine usage: */
387 if (!strMachineUsage.isEmpty())
388 m_strUsage += strMachineUsage;
389 }
390
391 /* Refresh tool-tip: */
392 m_strToolTip = m_sstrRow.arg(QString("<p style=white-space:pre><b>%1</b></p>").arg(m_fHostDrive ? m_strName : m_strLocation));
393 if (m_type == UIMediumType_HardDisk)
394 {
395 m_strToolTip += m_sstrRow.arg(VBoxGlobal::tr("<p style=white-space:pre>Type (Format): %1 (%2)</p>", "medium")
396 .arg(m_strHardDiskType).arg(m_strHardDiskFormat));
397 }
398 m_strToolTip += m_sstrRow.arg(VBoxGlobal::tr("<p>Attached to: %1</p>", "image")
399 .arg(m_strUsage.isNull() ? VBoxGlobal::tr("<i>Not Attached</i>", "image") : m_strUsage));
400 switch (m_state)
401 {
402 case KMediumState_NotCreated:
403 {
404 m_strToolTip += m_sstrRow.arg(VBoxGlobal::tr("<i>Checking accessibility...</i>", "medium"));
405 break;
406 }
407 case KMediumState_Inaccessible:
408 {
409 if (m_result.isOk())
410 {
411 /* Not Accessible: */
412 m_strToolTip += m_sstrRow.arg("<hr>") + m_sstrRow.arg(VBoxGlobal::highlight(m_strLastAccessError, true /* aToolTip */));
413 }
414 else
415 {
416 /* Accessibility check (eg GetState()) itself failed: */
417 m_strToolTip += m_sstrRow.arg("<hr>") + m_sstrRow.arg(VBoxGlobal::tr("Failed to check accessibility of disk image files.", "medium")) +
418 m_sstrRow.arg(UIErrorString::formatErrorInfo(m_result) + ".");
419 }
420 break;
421 }
422 default:
423 break;
424 }
425 }
426}
427
428void UIMedium::updateParentID()
429{
430 m_strParentId = nullID();
431 if (m_type == UIMediumType_HardDisk)
432 {
433 CMedium parentMedium = m_medium.GetParent();
434 if (!parentMedium.isNull())
435 m_strParentId = normalizedID(parentMedium.GetId());
436 }
437}
438
439QString UIMedium::toolTip(bool fNoDiffs /* = false */, bool fCheckRO /* = false */, bool fNullAllowed /* = false */) const
440{
441 QString strTip;
442
443 if (m_medium.isNull())
444 {
445 strTip = fNullAllowed ? m_sstrRow.arg(VBoxGlobal::tr("<b>No disk image file selected</b>", "medium")) +
446 m_sstrRow.arg(VBoxGlobal::tr("You can also change this while the machine is running.")) :
447 m_sstrRow.arg(VBoxGlobal::tr("<b>No disk image files available</b>", "medium")) +
448 m_sstrRow.arg(VBoxGlobal::tr("You can create or add disk image files in the virtual machine settings."));
449 }
450 else
451 {
452 unconst(this)->checkNoDiffs(fNoDiffs);
453
454 strTip = fNoDiffs ? m_noDiffs.toolTip : m_strToolTip;
455
456 if (fCheckRO && m_fReadOnly)
457 strTip += m_sstrRow.arg("<hr>") +
458 m_sstrRow.arg(VBoxGlobal::tr("Attaching this hard disk will be performed indirectly using "
459 "a newly created differencing hard disk.", "medium"));
460 }
461
462 return m_sstrTable.arg(strTip);
463}
464
465QPixmap UIMedium::icon(bool fNoDiffs /* = false */, bool fCheckRO /* = false */) const
466{
467 QPixmap pixmap;
468
469 if (state(fNoDiffs) == KMediumState_Inaccessible)
470 pixmap = result(fNoDiffs).isOk() ? vboxGlobal().warningIcon() : vboxGlobal().errorIcon();
471
472 if (fCheckRO && m_fReadOnly)
473 {
474 QIcon icon = UIIconPool::iconSet(":/hd_new_16px.png");
475 pixmap = VBoxGlobal::joinPixmaps(pixmap, icon.pixmap(icon.availableSizes().value(0, QSize(16, 16))));
476 }
477
478 return pixmap;
479}
480
481QString UIMedium::details(bool fNoDiffs /* = false */,
482 bool fPredictDiff /* = false */,
483 bool fUseHTML /* = false */) const
484{
485 /// @todo the below check is rough; if m_medium becomes uninitialized, any
486 // of getters called afterwards will also fail. The same relates to the
487 // root hard drive object (that will be the hard drive itself in case of
488 // non-differencing disks). However, this check was added to fix a
489 // particular use case: when the hard drive is a differencing hard drive and
490 // it happens to be discarded (and uninitialized) after this method is
491 // called but before we read all its properties (yes, it's possible!), the
492 // root object will be null and calling methods on it will assert in the
493 // debug builds. This check seems to be enough as a quick solution (fresh
494 // hard drive attachments will be re-read by a machine state change signal
495 // after the discard operation is finished, so the user will eventually see
496 // correct data), but in order to solve the problem properly we need to use
497 // exceptions everywhere (or check the result after every method call). See
498 // @bugref{2149}.
499
500 if (m_medium.isNull() || m_fHostDrive)
501 return m_strName;
502
503 if (!m_medium.isOk())
504 return QString();
505
506 QString strDetails, strText;
507
508 /* Note: root accessible only if medium enumerated: */
509 UIMedium rootMedium = root();
510 KMediumState eState = m_state;
511
512 if (m_type == UIMediumType_HardDisk)
513 {
514 if (fNoDiffs)
515 {
516 bool isDiff = (!fPredictDiff && parentID() != nullID()) || (fPredictDiff && m_fReadOnly);
517
518 strDetails = isDiff && fUseHTML ?
519 QString("<i>%1</i>, ").arg(rootMedium.m_strHardDiskType) :
520 QString("%1, ").arg(rootMedium.m_strHardDiskType);
521
522 eState = this->state(true /* fNoDiffs */);
523
524 if (rootMedium.m_state == KMediumState_NotCreated)
525 eState = KMediumState_NotCreated;
526 }
527 else
528 {
529 strDetails = QString("%1, ").arg(rootMedium.m_strHardDiskType);
530 }
531
532 /* Add encryption status: */
533 if (m_fEncrypted)
534 strDetails += QString("%1, ").arg(VBoxGlobal::tr("Encrypted", "medium"));
535 }
536
537 /// @todo prepend the details with the warning/error icon when not accessible
538
539 switch (eState)
540 {
541 case KMediumState_NotCreated:
542 strText = VBoxGlobal::tr("Checking...", "medium");
543 strDetails += fUseHTML ? QString("<i>%1</i>").arg(strText) : strText;
544 break;
545 case KMediumState_Inaccessible:
546 strText = VBoxGlobal::tr("Inaccessible", "medium");
547 strDetails += fUseHTML ? QString("<b>%1</b>").arg(strText) : strText;
548 break;
549 default:
550 strDetails += m_type == UIMediumType_HardDisk ? rootMedium.m_strLogicalSize : rootMedium.m_strSize;
551 break;
552 }
553
554 strDetails = fUseHTML ?
555 QString("%1 (<nobr>%2</nobr>)").arg(VBoxGlobal::locationForHTML(rootMedium.m_strName), strDetails) :
556 QString("%1 (%2)").arg(VBoxGlobal::locationForHTML(rootMedium.m_strName), strDetails);
557
558 return strDetails;
559}
560
561/* static */
562QString UIMedium::nullID()
563{
564 return m_sstrNullID;
565}
566
567/* static */
568QString UIMedium::normalizedID(const QString &strID)
569{
570 /* Handle wrong UUID (null/empty or invalid format): */
571 if (QUuid(strID).toString().remove('{').remove('}') != strID)
572 return nullID();
573 return strID;
574}
575
576/* static */
577bool UIMedium::isMediumAttachedToHiddenMachinesOnly(const UIMedium &medium)
578{
579 /* Iterate till the root: */
580 UIMedium mediumIterator = medium;
581 do
582 {
583 /* Ignore medium if its hidden
584 * or attached to hidden machines only: */
585 if (mediumIterator.isHidden())
586 return true;
587 /* Move iterator to parent: */
588 mediumIterator = mediumIterator.parent();
589 }
590 while (!mediumIterator.isNull());
591 /* False by default: */
592 return false;
593}
594
595UIMedium UIMedium::root() const
596{
597 /* Redirect call to VBoxGlobal: */
598 return vboxGlobal().medium(m_strRootId);
599}
600
601UIMedium UIMedium::parent() const
602{
603 /* Redirect call to VBoxGlobal: */
604 return vboxGlobal().medium(m_strParentId);
605}
606
607void UIMedium::checkNoDiffs(bool fNoDiffs)
608{
609 if (!fNoDiffs || m_noDiffs.isSet)
610 return;
611
612 m_noDiffs.toolTip = QString();
613
614 m_noDiffs.state = m_state;
615 for (UIMedium parentMedium = parent(); !parentMedium.isNull(); parentMedium = parentMedium.parent())
616 {
617 if (parentMedium.m_state == KMediumState_Inaccessible)
618 {
619 m_noDiffs.state = parentMedium.m_state;
620
621 if (m_noDiffs.toolTip.isNull())
622 m_noDiffs.toolTip = m_sstrRow.arg(VBoxGlobal::tr("Some of the files in this hard disk chain "
623 "are inaccessible. Please use the Virtual Medium "
624 "Manager to inspect these files.", "medium"));
625
626 if (!parentMedium.m_result.isOk())
627 {
628 m_noDiffs.result = parentMedium.m_result;
629 break;
630 }
631 }
632 }
633
634 if (parentID() != nullID() && !m_fReadOnly)
635 {
636 m_noDiffs.toolTip = root().tip() +
637 m_sstrRow.arg("<hr>") +
638 m_sstrRow.arg(VBoxGlobal::tr("This base hard disk is indirectly attached using "
639 "the following differencing hard disk:", "medium")) +
640 m_strToolTip + m_noDiffs.toolTip;
641 }
642
643 if (m_noDiffs.toolTip.isNull())
644 m_noDiffs.toolTip = m_strToolTip;
645
646 m_noDiffs.isSet = true;
647}
648
649/* static */
650QString UIMedium::mediumTypeToString(const CMedium &comMedium)
651{
652 if (!comMedium.GetParent().isNull())
653 {
654 Assert(comMedium.GetType() == KMediumType_Normal);
655 return QApplication::translate("VBoxGlobal", "Differencing", "MediumType");
656 }
657 return gpConverter->toString(comMedium.GetType());
658}
659
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette